From 13d05bc8458758ee39cb829098241e89616717ee Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Wed, 9 Sep 2015 22:15:21 -0700 Subject: ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60 Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd --- framework/src/onos/providers/openflow/app/app.xml | 33 + .../src/onos/providers/openflow/app/features.xml | 34 + framework/src/onos/providers/openflow/app/pom.xml | 81 +++ .../src/onos/providers/openflow/device/pom.xml | 39 ++ .../of/device/impl/OpenFlowDeviceProvider.java | 569 +++++++++++++++++ .../of/device/impl/PortStatsCollector.java | 113 ++++ .../provider/of/device/impl/package-info.java | 20 + .../of/device/impl/OpenFlowDeviceProviderTest.java | 402 ++++++++++++ framework/src/onos/providers/openflow/flow/pom.xml | 40 ++ .../provider/of/flow/impl/FlowEntryBuilder.java | 707 +++++++++++++++++++++ .../provider/of/flow/impl/FlowModBuilder.java | 444 +++++++++++++ .../provider/of/flow/impl/FlowModBuilderVer10.java | 230 +++++++ .../provider/of/flow/impl/FlowModBuilderVer13.java | 458 +++++++++++++ .../provider/of/flow/impl/FlowStatsCollector.java | 100 +++ .../of/flow/impl/NoMappingFoundException.java | 31 + .../of/flow/impl/OpenFlowRuleProvider.java | 453 +++++++++++++ .../provider/of/flow/impl/OpenFlowValueMapper.java | 152 +++++ .../provider/of/flow/impl/package-info.java | 20 + .../src/onos/providers/openflow/group/pom.xml | 34 + .../of/group/impl/GroupBucketEntryBuilder.java | 343 ++++++++++ .../provider/of/group/impl/GroupModBuilder.java | 376 +++++++++++ .../of/group/impl/GroupStatsCollector.java | 111 ++++ .../of/group/impl/OpenFlowGroupProvider.java | 366 +++++++++++ .../provider/of/group/impl/package-info.java | 20 + .../of/group/impl/OpenFlowGroupProviderTest.java | 397 ++++++++++++ .../src/onos/providers/openflow/meter/pom.xml | 34 + .../provider/of/meter/impl/MeterModBuilder.java | 159 +++++ .../of/meter/impl/MeterStatsCollector.java | 103 +++ .../of/meter/impl/OpenFlowMeterProvider.java | 393 ++++++++++++ .../provider/of/meter/impl/package-info.java | 20 + .../of/meter/impl/OpenFlowMeterProviderTest.java | 22 + .../src/onos/providers/openflow/packet/pom.xml | 34 + .../of/packet/impl/OpenFlowCorePacketContext.java | 99 +++ .../of/packet/impl/OpenFlowPacketProvider.java | 176 +++++ .../provider/of/packet/impl/package-info.java | 21 + .../of/packet/impl/OpenFlowPacketProviderTest.java | 431 +++++++++++++ framework/src/onos/providers/openflow/pom.xml | 63 ++ 37 files changed, 7128 insertions(+) create mode 100644 framework/src/onos/providers/openflow/app/app.xml create mode 100644 framework/src/onos/providers/openflow/app/features.xml create mode 100644 framework/src/onos/providers/openflow/app/pom.xml create mode 100644 framework/src/onos/providers/openflow/device/pom.xml create mode 100644 framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java create mode 100644 framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java create mode 100644 framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/package-info.java create mode 100644 framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java create mode 100644 framework/src/onos/providers/openflow/flow/pom.xml create mode 100644 framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java create mode 100644 framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java create mode 100644 framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java create mode 100644 framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java create mode 100644 framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java create mode 100644 framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NoMappingFoundException.java create mode 100644 framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java create mode 100644 framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowValueMapper.java create mode 100644 framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/package-info.java create mode 100644 framework/src/onos/providers/openflow/group/pom.xml create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java create mode 100644 framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java create mode 100644 framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java create mode 100644 framework/src/onos/providers/openflow/meter/pom.xml create mode 100644 framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java create mode 100644 framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterStatsCollector.java create mode 100644 framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java create mode 100644 framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/package-info.java create mode 100644 framework/src/onos/providers/openflow/meter/src/test/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProviderTest.java create mode 100644 framework/src/onos/providers/openflow/packet/pom.xml create mode 100644 framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java create mode 100644 framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java create mode 100644 framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/package-info.java create mode 100644 framework/src/onos/providers/openflow/packet/src/test/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProviderTest.java create mode 100644 framework/src/onos/providers/openflow/pom.xml (limited to 'framework/src/onos/providers/openflow') diff --git a/framework/src/onos/providers/openflow/app/app.xml b/framework/src/onos/providers/openflow/app/app.xml new file mode 100644 index 00000000..e54d1a8a --- /dev/null +++ b/framework/src/onos/providers/openflow/app/app.xml @@ -0,0 +1,33 @@ + + + + ${project.description} + + mvn:${project.groupId}/onos-of-api/${project.version} + mvn:${project.groupId}/onos-of-ctl/${project.version} + mvn:${project.groupId}/onos-drivers/${project.version} + + mvn:${project.groupId}/onos-lldp-provider/${project.version} + mvn:${project.groupId}/onos-host-provider/${project.version} + mvn:${project.groupId}/onos-of-provider-device/${project.version} + mvn:${project.groupId}/onos-of-provider-packet/${project.version} + mvn:${project.groupId}/onos-of-provider-flow/${project.version} + mvn:${project.groupId}/onos-of-provider-group/${project.version} + mvn:${project.groupId}/onos-of-provider-meter/${project.version} + diff --git a/framework/src/onos/providers/openflow/app/features.xml b/framework/src/onos/providers/openflow/app/features.xml new file mode 100644 index 00000000..7c410172 --- /dev/null +++ b/framework/src/onos/providers/openflow/app/features.xml @@ -0,0 +1,34 @@ + + + + mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features + + onos-api + mvn:io.netty/netty/3.9.2.Final + mvn:${project.groupId}/onos-of-api/${project.version} + mvn:${project.groupId}/onos-of-ctl/${project.version} + + mvn:${project.groupId}/onos-lldp-provider/${project.version} + mvn:${project.groupId}/onos-host-provider/${project.version} + mvn:${project.groupId}/onos-of-provider-device/${project.version} + mvn:${project.groupId}/onos-of-provider-packet/${project.version} + mvn:${project.groupId}/onos-of-provider-flow/${project.version} + mvn:${project.groupId}/onos-of-provider-group/${project.version} + mvn:${project.groupId}/onos-of-provider-meter/${project.version} + + diff --git a/framework/src/onos/providers/openflow/app/pom.xml b/framework/src/onos/providers/openflow/app/pom.xml new file mode 100644 index 00000000..62e5eb87 --- /dev/null +++ b/framework/src/onos/providers/openflow/app/pom.xml @@ -0,0 +1,81 @@ + + + + 4.0.0 + + + org.onosproject + onos-of-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-openflow + pom + + OpenFlow protocol southbound providers + + + + org.onosproject + onos-of-api + + + org.onosproject + onos-of-ctl + ${project.version} + + + org.onosproject + onos-drivers + ${project.version} + + + org.onosproject + onos-of-provider-device + ${project.version} + + + org.onosproject + onos-of-provider-packet + ${project.version} + + + org.onosproject + onos-of-provider-flow + ${project.version} + + + org.onosproject + onos-of-provider-group + ${project.version} + + + org.onosproject + onos-lldp-provider + ${project.version} + + + org.onosproject + onos-host-provider + ${project.version} + + + + diff --git a/framework/src/onos/providers/openflow/device/pom.xml b/framework/src/onos/providers/openflow/device/pom.xml new file mode 100644 index 00000000..5f27d42b --- /dev/null +++ b/framework/src/onos/providers/openflow/device/pom.xml @@ -0,0 +1,39 @@ + + + + 4.0.0 + + + org.onosproject + onos-of-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-of-provider-device + bundle + + ONOS OpenFlow protocol device provider + + + org.osgi + org.osgi.compendium + + + diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java new file mode 100644 index 00000000..cb19dc52 --- /dev/null +++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java @@ -0,0 +1,569 @@ +/* + * Copyright 2014-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.provider.of.device.impl; + +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Modified; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onlab.packet.ChassisId; +import org.onlab.util.Frequency; +import org.onosproject.cfg.ComponentConfigService; +import org.onlab.util.Spectrum; +import org.onosproject.net.AnnotationKeys; +import org.onosproject.net.ChannelSpacing; +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.DeviceId; +import org.onosproject.net.GridType; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.OchSignal; +import org.onosproject.net.OduSignalType; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.SparseAnnotations; +import org.onosproject.net.device.DefaultDeviceDescription; +import org.onosproject.net.device.DefaultPortDescription; +import org.onosproject.net.device.DefaultPortStatistics; +import org.onosproject.net.device.DeviceDescription; +import org.onosproject.net.device.DeviceProvider; +import org.onosproject.net.device.DeviceProviderRegistry; +import org.onosproject.net.device.DeviceProviderService; +import org.onosproject.net.device.OchPortDescription; +import org.onosproject.net.device.OmsPortDescription; +import org.onosproject.net.device.PortDescription; +import org.onosproject.net.device.PortStatistics; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowEventListener; +import org.onosproject.openflow.controller.OpenFlowOpticalSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitchListener; +import org.onosproject.openflow.controller.PortDescPropertyType; +import org.onosproject.openflow.controller.RoleState; +import org.osgi.service.component.ComponentContext; +import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsEntry; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortConfig; +import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport; +import org.projectfloodlight.openflow.protocol.OFPortFeatures; +import org.projectfloodlight.openflow.protocol.OFPortOptical; +import org.projectfloodlight.openflow.protocol.OFPortReason; +import org.projectfloodlight.openflow.protocol.OFPortState; +import org.projectfloodlight.openflow.protocol.OFPortStatsEntry; +import org.projectfloodlight.openflow.protocol.OFPortStatsReply; +import org.projectfloodlight.openflow.protocol.OFPortStatus; +import org.projectfloodlight.openflow.protocol.OFStatsReply; +import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags; +import org.projectfloodlight.openflow.protocol.OFStatsType; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.types.PortSpeed; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.onlab.util.Tools.get; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.Port.Type.COPPER; +import static org.onosproject.net.Port.Type.FIBER; +import static org.onosproject.openflow.controller.Dpid.dpid; +import static org.onosproject.openflow.controller.Dpid.uri; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Provider which uses an OpenFlow controller to detect network + * infrastructure devices. + */ +@Component(immediate = true) +public class OpenFlowDeviceProvider extends AbstractProvider implements DeviceProvider { + + private static final Logger LOG = getLogger(OpenFlowDeviceProvider.class); + private static final long MBPS = 1_000 * 1_000; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceProviderRegistry providerRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenFlowController controller; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ComponentConfigService cfgService; + + private DeviceProviderService providerService; + + private final InternalDeviceProvider listener = new InternalDeviceProvider(); + + // TODO: We need to make the poll interval configurable. + static final int POLL_INTERVAL = 5; + @Property(name = "PortStatsPollFrequency", intValue = POLL_INTERVAL, + label = "Frequency (in seconds) for polling switch Port statistics") + private int portStatsPollFrequency = POLL_INTERVAL; + + private HashMap collectors = Maps.newHashMap(); + + /** + * Creates an OpenFlow device provider. + */ + public OpenFlowDeviceProvider() { + super(new ProviderId("of", "org.onosproject.provider.openflow")); + } + + @Activate + public void activate(ComponentContext context) { + cfgService.registerProperties(getClass()); + providerService = providerRegistry.register(this); + controller.addListener(listener); + controller.addEventListener(listener); + for (OpenFlowSwitch sw : controller.getSwitches()) { + try { + listener.switchAdded(new Dpid(sw.getId())); + } catch (Exception e) { + LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage()); + LOG.debug("Error details:", e); + // disconnect to trigger switch-add later + sw.disconnectSwitch(); + } + PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency); + psc.start(); + collectors.put(new Dpid(sw.getId()), psc); + } + LOG.info("Started"); + } + + @Deactivate + public void deactivate(ComponentContext context) { + cfgService.unregisterProperties(getClass(), false); + providerRegistry.unregister(this); + controller.removeListener(listener); + collectors.values().forEach(PortStatsCollector::stop); + providerService = null; + LOG.info("Stopped"); + } + + @Modified + public void modified(ComponentContext context) { + Dictionary properties = context.getProperties(); + int newPortStatsPollFrequency; + try { + String s = get(properties, "PortStatsPollFrequency"); + newPortStatsPollFrequency = isNullOrEmpty(s) ? portStatsPollFrequency : Integer.parseInt(s.trim()); + + } catch (NumberFormatException | ClassCastException e) { + newPortStatsPollFrequency = portStatsPollFrequency; + } + + if (newPortStatsPollFrequency != portStatsPollFrequency) { + portStatsPollFrequency = newPortStatsPollFrequency; + collectors.values().forEach(psc -> psc.adjustPollInterval(portStatsPollFrequency)); + } + + LOG.info("Settings: portStatsPollFrequency={}", portStatsPollFrequency); + } + + @Override + public boolean isReachable(DeviceId deviceId) { + OpenFlowSwitch sw = controller.getSwitch(dpid(deviceId.uri())); + if (sw == null || !sw.isConnected()) { + return false; + } + return true; + } + + @Override + public void triggerProbe(DeviceId deviceId) { + LOG.debug("Triggering probe on device {}", deviceId); + + final Dpid dpid = dpid(deviceId.uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + if (sw == null || !sw.isConnected()) { + LOG.error("Failed to probe device {} on sw={}", deviceId, sw); + providerService.deviceDisconnected(deviceId); + return; + } else { + LOG.trace("Confirmed device {} connection", deviceId); + } + + // Prompt an update of port information. We can use any XID for this. + OFFactory fact = sw.factory(); + switch (fact.getVersion()) { + case OF_10: + sw.sendMsg(fact.buildFeaturesRequest().setXid(0).build()); + break; + case OF_13: + sw.sendMsg(fact.buildPortDescStatsRequest().setXid(0).build()); + break; + default: + LOG.warn("Unhandled protocol version"); + } + } + + @Override + public void roleChanged(DeviceId deviceId, MastershipRole newRole) { + switch (newRole) { + case MASTER: + controller.setRole(dpid(deviceId.uri()), RoleState.MASTER); + break; + case STANDBY: + controller.setRole(dpid(deviceId.uri()), RoleState.EQUAL); + break; + case NONE: + controller.setRole(dpid(deviceId.uri()), RoleState.SLAVE); + break; + default: + LOG.error("Unknown Mastership state : {}", newRole); + + } + LOG.debug("Accepting mastership role change for device {}", deviceId); + } + + private void pushPortMetrics(Dpid dpid, List portStatsEntries) { + DeviceId deviceId = DeviceId.deviceId(dpid.uri(dpid)); + Collection stats = buildPortStatistics(deviceId, portStatsEntries); + providerService.updatePortStatistics(deviceId, stats); + } + + private Collection buildPortStatistics(DeviceId deviceId, + List entries) { + HashSet stats = Sets.newHashSet(); + + for (OFPortStatsEntry entry : entries) { + try { + if (entry == null || entry.getPortNo() == null || entry.getPortNo().getPortNumber() < 0) { + continue; + } + DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder(); + DefaultPortStatistics stat = builder.setDeviceId(deviceId) + .setPort(entry.getPortNo().getPortNumber()) + .setPacketsReceived(entry.getRxPackets().getValue()) + .setPacketsSent(entry.getTxPackets().getValue()) + .setBytesReceived(entry.getRxBytes().getValue()) + .setBytesSent(entry.getTxBytes().getValue()) + .setPacketsRxDropped(entry.getRxDropped().getValue()) + .setPacketsTxDropped(entry.getTxDropped().getValue()) + .setPacketsRxErrors(entry.getRxErrors().getValue()) + .setPacketsTxErrors(entry.getTxErrors().getValue()) + .setDurationSec(entry.getVersion() == OFVersion.OF_10 ? 0 : entry.getDurationSec()) + .setDurationNano(entry.getVersion() == OFVersion.OF_10 ? 0 : entry.getDurationNsec()) + .build(); + + stats.add(stat); + } catch (Exception e) { + LOG.warn("Unable to process port stats", e); + } + } + + return Collections.unmodifiableSet(stats); + + } + + private class InternalDeviceProvider implements OpenFlowSwitchListener, OpenFlowEventListener { + + private HashMap> portStatsReplies = new HashMap<>(); + + @Override + public void switchAdded(Dpid dpid) { + if (providerService == null) { + return; + } + DeviceId did = deviceId(uri(dpid)); + OpenFlowSwitch sw = controller.getSwitch(dpid); + + ChassisId cId = new ChassisId(dpid.value()); + + SparseAnnotations annotations = DefaultAnnotations.builder() + .set("protocol", sw.factory().getVersion().toString()) + .set("channelId", sw.channelId()) + .build(); + + DeviceDescription description = + new DefaultDeviceDescription(did.uri(), sw.deviceType(), + sw.manufacturerDescription(), + sw.hardwareDescription(), + sw.softwareDescription(), + sw.serialNumber(), + cId, annotations); + providerService.deviceConnected(did, description); + providerService.updatePorts(did, buildPortDescriptions(sw)); + + PortStatsCollector psc = + new PortStatsCollector(controller.getSwitch(dpid), portStatsPollFrequency); + psc.start(); + collectors.put(dpid, psc); + } + + @Override + public void switchRemoved(Dpid dpid) { + if (providerService == null) { + return; + } + providerService.deviceDisconnected(deviceId(uri(dpid))); + + PortStatsCollector collector = collectors.remove(dpid); + if (collector != null) { + collector.stop(); + } + } + + @Override + public void switchChanged(Dpid dpid) { + if (providerService == null) { + return; + } + DeviceId did = deviceId(uri(dpid)); + OpenFlowSwitch sw = controller.getSwitch(dpid); + providerService.updatePorts(did, buildPortDescriptions(sw)); + } + + @Override + public void portChanged(Dpid dpid, OFPortStatus status) { + PortDescription portDescription = buildPortDescription(status); + providerService.portStatusChanged(deviceId(uri(dpid)), portDescription); + } + + @Override + public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) { + MastershipRole request = roleOf(requested); + MastershipRole reply = roleOf(response); + + providerService.receivedRoleReply(deviceId(uri(dpid)), request, reply); + } + + /** + * Translates a RoleState to the corresponding MastershipRole. + * + * @param response role state + * @return a MastershipRole + */ + private MastershipRole roleOf(RoleState response) { + switch (response) { + case MASTER: + return MastershipRole.MASTER; + case EQUAL: + return MastershipRole.STANDBY; + case SLAVE: + return MastershipRole.NONE; + default: + LOG.warn("unknown role {}", response); + return null; + } + } + + /** + * Builds a list of port descriptions for a given list of ports. + * + * @return list of portdescriptions + */ + private List buildPortDescriptions(OpenFlowSwitch sw) { + final List portDescs = new ArrayList<>(sw.getPorts().size()); + sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port))); + + OpenFlowOpticalSwitch opsw; + switch (sw.deviceType()) { + case ROADM: + opsw = (OpenFlowOpticalSwitch) sw; + opsw.getPortTypes().forEach(type -> { + opsw.getPortsOf(type).forEach( + op -> { + portDescs.add(buildPortDescription(type, (OFPortOptical) op)); + } + ); + }); + break; + case FIBER_SWITCH: + opsw = (OpenFlowOpticalSwitch) sw; + opsw.getPortTypes().forEach(type -> { + opsw.getPortsOf(type).forEach( + op -> { + portDescs.add(buildPortDescription((OFCalientPortDescStatsEntry) op)); + } + ); + }); + break; + default: + break; + } + + return portDescs; + } + + /** + * Creates an annotation for the port name if one is available. + * + * @param port description of the port + * @return annotation containing the port name if one is found, + * null otherwise + */ + private SparseAnnotations makePortNameAnnotation(String port) { + SparseAnnotations annotations = null; + String portName = Strings.emptyToNull(port); + if (portName != null) { + annotations = DefaultAnnotations.builder() + .set(AnnotationKeys.PORT_NAME, portName).build(); + } + return annotations; + } + + /** + * Build a portDescription from a given Ethernet port description. + * + * @param port the port to build from. + * @return portDescription for the port. + */ + private PortDescription buildPortDescription(OFPortDesc port) { + PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber()); + boolean enabled = + !port.getState().contains(OFPortState.LINK_DOWN) && + !port.getConfig().contains(OFPortConfig.PORT_DOWN); + Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER; + SparseAnnotations annotations = makePortNameAnnotation(port.getName()); + return new DefaultPortDescription(portNo, enabled, type, + portSpeed(port), annotations); + } + + /** + * Build a portDescription from a given a port description describing some + * Optical port. + * + * @param port description property type. + * @param port the port to build from. + * @return portDescription for the port. + */ + private PortDescription buildPortDescription(PortDescPropertyType ptype, OFPortOptical port) { + checkArgument(port.getDesc().size() >= 1); + + // Minimally functional fixture. This needs to be fixed as we add better support. + PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber()); + + boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) + && !port.getConfig().contains(OFPortConfig.PORT_DOWN); + SparseAnnotations annotations = makePortNameAnnotation(port.getName()); + + if (port.getVersion() == OFVersion.OF_13 + && ptype == PortDescPropertyType.OPTICAL_TRANSPORT) { + // At this point, not much is carried in the optical port message. + LOG.debug("Optical transport port message {}", port.toString()); + } else { + // removable once 1.4+ support complete. + LOG.debug("Unsupported optical port properties"); + } + + OFPortDescPropOpticalTransport desc = port.getDesc().get(0); + switch (desc.getPortSignalType()) { + // FIXME: use constants once loxi has full optical extensions + case 2: // OMS port + // Assume complete optical spectrum and 50 GHz grid + // LINC-OE is only supported optical OF device for now + return new OmsPortDescription(portNo, enabled, + Spectrum.U_BAND_MIN, Spectrum.O_BAND_MAX, Frequency.ofGHz(50), annotations); + case 5: // OCH port + OchSignal signal = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 0, 4); + return new OchPortDescription(portNo, enabled, OduSignalType.ODU4, + true, signal, annotations); + default: + break; + } + + return new DefaultPortDescription(portNo, enabled, FIBER, 0, annotations); + } + + /** + * Build a portDescription from a given port description describing a fiber switch optical port. + * + * @param port description property type. + * @param port the port to build from. + * @return portDescription for the port. + */ + private PortDescription buildPortDescription(OFCalientPortDescStatsEntry port) { + PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber()); + + // FIXME when Calient OF agent reports port status + boolean enabled = true; + SparseAnnotations annotations = makePortNameAnnotation(port.getName()); + + // S160 data sheet + // Wavelength range: 1260 - 1630 nm, grid is irrelevant for this type of switch + return new OmsPortDescription(portNo, enabled, + Spectrum.U_BAND_MIN, Spectrum.O_BAND_MAX, Frequency.ofGHz(100), annotations); + } + + private PortDescription buildPortDescription(OFPortStatus status) { + OFPortDesc port = status.getDesc(); + if (status.getReason() != OFPortReason.DELETE) { + return buildPortDescription(port); + } else { + PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber()); + Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER; + SparseAnnotations annotations = makePortNameAnnotation(port.getName()); + return new DefaultPortDescription(portNo, false, type, + portSpeed(port), annotations); + } + } + + private long portSpeed(OFPortDesc port) { + if (port.getVersion() == OFVersion.OF_13) { + return port.getCurrSpeed() / MBPS; + } + + PortSpeed portSpeed = PortSpeed.SPEED_NONE; + for (OFPortFeatures feat : port.getCurr()) { + portSpeed = PortSpeed.max(portSpeed, feat.getPortSpeed()); + } + return portSpeed.getSpeedBps() / MBPS; + } + + @Override + public void handleMessage(Dpid dpid, OFMessage msg) { + switch (msg.getType()) { + case STATS_REPLY: + if (((OFStatsReply) msg).getStatsType() == OFStatsType.PORT) { + OFPortStatsReply portStatsReply = (OFPortStatsReply) msg; + List portStatsReplyList = portStatsReplies.get(dpid); + if (portStatsReplyList == null) { + portStatsReplyList = Lists.newArrayList(); + } + portStatsReplyList.addAll(portStatsReply.getEntries()); + portStatsReplies.put(dpid, portStatsReplyList); + if (!portStatsReply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) { + pushPortMetrics(dpid, portStatsReplies.get(dpid)); + portStatsReplies.get(dpid).clear(); + } + } + break; + default: + break; + } + } + } + +} diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java new file mode 100644 index 00000000..8383fa3f --- /dev/null +++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java @@ -0,0 +1,113 @@ +/* + * 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.provider.of.device.impl; + +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.util.Timer; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFPortStatsRequest; +import org.projectfloodlight.openflow.types.OFPort; +import org.slf4j.Logger; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import static org.slf4j.LoggerFactory.getLogger; + +/* + * Sends Group Stats Request and collect the group statistics with a time interval. + */ +public class PortStatsCollector implements TimerTask { + + // TODO: Refactoring is required using ScheduledExecutorService + + private final HashedWheelTimer timer = Timer.getTimer(); + private final OpenFlowSwitch sw; + private final Logger log = getLogger(getClass()); + private int refreshInterval; + private final AtomicLong xidAtomic = new AtomicLong(1); + + private Timeout timeout; + private volatile boolean stopped; + + /** + * Creates a GroupStatsCollector object. + * + * @param sw Open Flow switch + * @param interval time interval for collecting group statistic + */ + public PortStatsCollector(OpenFlowSwitch sw, int interval) { + this.sw = sw; + this.refreshInterval = interval; + } + + @Override + public void run(Timeout to) throws Exception { + if (stopped || timeout.isCancelled()) { + return; + } + log.trace("Collecting stats for {}", sw.getStringId()); + + sendPortStatistic(); + + if (!stopped && !timeout.isCancelled()) { + log.trace("Scheduling stats collection in {} seconds for {}", + this.refreshInterval, this.sw.getStringId()); + timeout.getTimer().newTimeout(this, refreshInterval, TimeUnit.SECONDS); + } + } + + synchronized void adjustPollInterval(int pollInterval) { + this.refreshInterval = pollInterval; + // task.cancel(); + // task = new InternalTimerTask(); + // timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000); + } + + private void sendPortStatistic() { + if (sw.getRole() != RoleState.MASTER) { + return; + } + Long statsXid = xidAtomic.getAndIncrement(); + OFPortStatsRequest statsRequest = sw.factory().buildPortStatsRequest() + .setPortNo(OFPort.ANY) + .setXid(statsXid) + .build(); + sw.sendMsg(statsRequest); + } + + /** + * Starts the collector. + */ + public synchronized void start() { + log.info("Starting Port Stats collection thread for {}", sw.getStringId()); + stopped = false; + timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS); + } + + /** + * Stops the collector. + */ + public synchronized void stop() { + log.info("Stopping Port Stats collection thread for {}", sw.getStringId()); + stopped = true; + timeout.cancel(); + } +} diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/package-info.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/package-info.java new file mode 100644 index 00000000..9376b47d --- /dev/null +++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2014 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. + */ + +/** + * Provider that uses OpenFlow controller as a means of infrastructure device discovery. + */ +package org.onosproject.provider.of.device.impl; diff --git a/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java new file mode 100644 index 00000000..7b4d7922 --- /dev/null +++ b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java @@ -0,0 +1,402 @@ +/* + * Copyright 2014-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.provider.of.device.impl; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.cfg.ComponentConfigAdapter; +import org.onosproject.net.DefaultDevice; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.device.DeviceDescription; +import org.onosproject.net.device.DeviceProvider; +import org.onosproject.net.device.DeviceProviderRegistry; +import org.onosproject.net.device.DeviceProviderService; +import org.onosproject.net.device.PortDescription; +import org.onosproject.net.device.PortStatistics; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowEventListener; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitchListener; +import org.onosproject.openflow.controller.PacketListener; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.OFPortReason; +import org.projectfloodlight.openflow.protocol.OFPortStatus; +import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10; +import org.projectfloodlight.openflow.types.OFPort; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.*; +import static org.onosproject.net.Device.Type.SWITCH; +import static org.onosproject.net.MastershipRole.*; + +public class OpenFlowDeviceProviderTest { + + private static final ProviderId PID = new ProviderId("of", "test"); + private static final DeviceId DID1 = DeviceId.deviceId("of:0000000000000001"); + private static final Dpid DPID1 = Dpid.dpid(DID1.uri()); + + private static final OFPortDesc PD1 = portDesc(1); + private static final OFPortDesc PD2 = portDesc(2); + private static final OFPortDesc PD3 = portDesc(3); + + private static final List PLIST = Lists.newArrayList(PD1, PD2); + + private static final Device DEV1 = + new DefaultDevice(PID, DID1, SWITCH, "", "", "", "", null); + + private static final TestOpenFlowSwitch SW1 = new TestOpenFlowSwitch(); + + private final OpenFlowDeviceProvider provider = new OpenFlowDeviceProvider(); + private final TestDeviceRegistry registry = new TestDeviceRegistry(); + private final TestController controller = new TestController(); + + @Before + public void startUp() { + provider.providerRegistry = registry; + provider.controller = controller; + provider.cfgService = new ComponentConfigAdapter(); + controller.switchMap.put(DPID1, SW1); + provider.activate(null); + assertNotNull("provider should be registered", registry.provider); + assertNotNull("listener should be registered", controller.listener); + assertEquals("devices not added", 1, registry.connected.size()); + assertEquals("ports not added", 2, registry.ports.get(DID1).size()); + } + + @After + public void tearDown() { + provider.deactivate(null); + assertNull("listener should be removed", controller.listener); + provider.controller = null; + provider.providerRegistry = null; + } + + @Test + public void roleChanged() { + provider.roleChanged(DID1, MASTER); + assertEquals("Should be MASTER", RoleState.MASTER, controller.roleMap.get(DPID1)); + provider.roleChanged(DID1, STANDBY); + assertEquals("Should be EQUAL", RoleState.EQUAL, controller.roleMap.get(DPID1)); + provider.roleChanged(DID1, NONE); + assertEquals("Should be SLAVE", RoleState.SLAVE, controller.roleMap.get(DPID1)); + } + + @Test + public void triggerProbe() { + + } + + @Test + public void switchRemoved() { + controller.listener.switchRemoved(DPID1); + assertTrue("device not removed", registry.connected.isEmpty()); + } + + @Test + public void portChanged() { + OFPortStatus stat = SW1.factory().buildPortStatus() + .setReason(OFPortReason.ADD) + .setDesc(PD3) + .build(); + controller.listener.portChanged(DPID1, stat); + assertNotNull("never went throught the provider service", registry.descr); + assertEquals("port status unhandled", 3, registry.ports.get(DID1).size()); + } + + @Test + public void receivedRoleReply() { + // check translation capabilities + controller.listener.receivedRoleReply(DPID1, RoleState.MASTER, RoleState.MASTER); + assertEquals("wrong role reported", DPID1, registry.roles.get(MASTER)); + controller.listener.receivedRoleReply(DPID1, RoleState.EQUAL, RoleState.MASTER); + assertEquals("wrong role reported", DPID1, registry.roles.get(STANDBY)); + controller.listener.receivedRoleReply(DPID1, RoleState.SLAVE, RoleState.MASTER); + assertEquals("wrong role reported", DPID1, registry.roles.get(NONE)); + } + + private static OFPortDesc portDesc(int port) { + OFPortDesc.Builder builder = OFFactoryVer10.INSTANCE.buildPortDesc(); + builder.setPortNo(OFPort.of(port)); + + return builder.build(); + } + + private class TestDeviceRegistry implements DeviceProviderRegistry { + DeviceProvider provider; + + Set connected = new HashSet<>(); + Multimap ports = HashMultimap.create(); + PortDescription descr = null; + Map roles = new HashMap<>(); + + @Override + public DeviceProviderService register(DeviceProvider provider) { + this.provider = provider; + return new TestProviderService(); + } + + @Override + public void unregister(DeviceProvider provider) { + } + + @Override + public Set getProviders() { + return null; + } + + private class TestProviderService implements DeviceProviderService { + + @Override + public DeviceProvider provider() { + return null; + } + + @Override + public void deviceConnected(DeviceId deviceId, + DeviceDescription deviceDescription) { + connected.add(deviceId); + } + + @Override + public void deviceDisconnected(DeviceId deviceId) { + connected.remove(deviceId); + ports.removeAll(deviceId); + } + + @Override + public void updatePorts(DeviceId deviceId, + List portDescriptions) { + for (PortDescription p : portDescriptions) { + ports.put(deviceId, p); + } + } + + @Override + public void portStatusChanged(DeviceId deviceId, + PortDescription portDescription) { + ports.put(deviceId, portDescription); + descr = portDescription; + } + + @Override + public void receivedRoleReply(DeviceId deviceId, + MastershipRole requested, MastershipRole response) { + roles.put(requested, Dpid.dpid(deviceId.uri())); + } + + @Override + public void updatePortStatistics(DeviceId deviceId, Collection portStatistics) { + + } + + } + } + + private class TestController implements OpenFlowController { + OpenFlowSwitchListener listener = null; + Map roleMap = new HashMap(); + Map switchMap = new HashMap(); + + @Override + public Iterable getSwitches() { + return switchMap.values(); + } + + @Override + public Iterable getMasterSwitches() { + return null; + } + + @Override + public Iterable getEqualSwitches() { + return null; + } + + @Override + public OpenFlowSwitch getSwitch(Dpid dpid) { + return switchMap.get(dpid); + } + + @Override + public OpenFlowSwitch getMasterSwitch(Dpid dpid) { + return null; + } + + @Override + public OpenFlowSwitch getEqualSwitch(Dpid dpid) { + + return null; + } + + @Override + public void addListener(OpenFlowSwitchListener listener) { + this.listener = listener; + } + + @Override + public void removeListener(OpenFlowSwitchListener listener) { + this.listener = null; + } + + @Override + public void addPacketListener(int priority, PacketListener listener) { + } + + @Override + public void removePacketListener(PacketListener listener) { + } + + @Override + public void addEventListener(OpenFlowEventListener listener) { + } + + @Override + public void removeEventListener(OpenFlowEventListener listener) { + } + + @Override + public void write(Dpid dpid, OFMessage msg) { + } + + @Override + public void processPacket(Dpid dpid, OFMessage msg) { + } + + @Override + public void setRole(Dpid dpid, RoleState role) { + roleMap.put(dpid, role); + } + } + + private static class TestOpenFlowSwitch implements OpenFlowSwitch { + + RoleState state; + List sent = new ArrayList(); + OFFactory factory = OFFactoryVer10.INSTANCE; + + @Override + public void sendMsg(OFMessage msg) { + sent.add(msg); + } + + @Override + public void sendMsg(List msgs) { + } + + @Override + public void handleMessage(OFMessage fromSwitch) { + } + + @Override + public void setRole(RoleState role) { + state = role; + } + + @Override + public RoleState getRole() { + return state; + } + + @Override + public List getPorts() { + return PLIST; + } + + @Override + public OFFactory factory() { + return factory; + } + + @Override + public String getStringId() { + return null; + } + + @Override + public long getId() { + return DPID1.value(); + } + + @Override + public String manufacturerDescription() { + return null; + } + + @Override + public String datapathDescription() { + return null; + } + + @Override + public String hardwareDescription() { + return null; + } + + @Override + public String softwareDescription() { + return null; + } + + @Override + public String serialNumber() { + return null; + } + + @Override + public boolean isConnected() { + return true; + } + + @Override + public void disconnectSwitch() { + } + + @Override + public Device.Type deviceType() { + return Device.Type.SWITCH; + } + + @Override + public void returnRoleReply(RoleState requested, RoleState response) { + } + + @Override + public String channelId() { + return "1.2.3.4:1"; + } + + } + +} diff --git a/framework/src/onos/providers/openflow/flow/pom.xml b/framework/src/onos/providers/openflow/flow/pom.xml new file mode 100644 index 00000000..24c430e3 --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + + org.onosproject + onos-of-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-of-provider-flow + bundle + + ONOS OpenFlow protocol flow provider + + + + org.osgi + org.osgi.compendium + + + diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java new file mode 100644 index 00000000..f238bdb1 --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java @@ -0,0 +1,707 @@ +/* + * Copyright 2014-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.provider.of.flow.impl; + +import static org.onosproject.net.flow.criteria.Criteria.matchLambda; +import static org.onosproject.net.flow.criteria.Criteria.matchOchSignalType; +import static org.onosproject.provider.of.flow.impl.OpenFlowValueMapper.lookupChannelSpacing; +import static org.onosproject.provider.of.flow.impl.OpenFlowValueMapper.lookupGridType; +import static org.onosproject.provider.of.flow.impl.OpenFlowValueMapper.lookupOchSignalType; +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.List; + +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.Ip6Address; +import org.onlab.packet.Ip6Prefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.MplsLabel; +import org.onlab.packet.TpPort; +import org.onlab.packet.VlanId; +import org.onosproject.core.DefaultGroupId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Lambda; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultFlowEntry; +import org.onosproject.net.flow.DefaultFlowRule; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowEntry; +import org.onosproject.net.flow.FlowEntry.FlowEntryState; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.openflow.controller.Dpid; +import org.projectfloodlight.openflow.protocol.OFFlowMod; +import org.projectfloodlight.openflow.protocol.OFFlowRemoved; +import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.action.OFActionCircuit; +import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter; +import org.projectfloodlight.openflow.protocol.action.OFActionGroup; +import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls; +import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst; +import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc; +import org.projectfloodlight.openflow.protocol.action.OFActionSetField; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc; +import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp; +import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid; +import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; +import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions; +import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable; +import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions; +import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteMetadata; +import org.projectfloodlight.openflow.protocol.match.Match; +import org.projectfloodlight.openflow.protocol.match.MatchField; +import org.projectfloodlight.openflow.protocol.oxm.OFOxm; +import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic; +import org.projectfloodlight.openflow.protocol.ver13.OFFactoryVer13; +import org.projectfloodlight.openflow.types.CircuitSignalID; +import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.Masked; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.TransportPort; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U64; +import org.projectfloodlight.openflow.types.U8; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.slf4j.Logger; + +import com.google.common.collect.Lists; + +public class FlowEntryBuilder { + private final Logger log = getLogger(getClass()); + + private final OFFlowStatsEntry stat; + private final OFFlowRemoved removed; + private final OFFlowMod flowMod; + + private final Match match; + + // All actions are contained in an OFInstruction. For OF1.0 + // the instruction type is apply instruction (immediate set in ONOS speak) + private final List instructions; + + private final Dpid dpid; + + public enum FlowType { STAT, REMOVED, MOD } + + private final FlowType type; + + public FlowEntryBuilder(Dpid dpid, OFFlowStatsEntry entry) { + this.stat = entry; + this.match = entry.getMatch(); + this.instructions = getInstructions(entry); + this.dpid = dpid; + this.removed = null; + this.flowMod = null; + this.type = FlowType.STAT; + } + + public FlowEntryBuilder(Dpid dpid, OFFlowRemoved removed) { + this.match = removed.getMatch(); + this.removed = removed; + + this.dpid = dpid; + this.instructions = null; + this.stat = null; + this.flowMod = null; + this.type = FlowType.REMOVED; + + } + + public FlowEntryBuilder(Dpid dpid, OFFlowMod fm) { + this.match = fm.getMatch(); + this.dpid = dpid; + this.instructions = getInstructions(fm); + this.type = FlowType.MOD; + this.flowMod = fm; + this.stat = null; + this.removed = null; + } + + public FlowEntry build(FlowEntryState... state) { + FlowRule rule; + switch (this.type) { + case STAT: + rule = DefaultFlowRule.builder() + .forDevice(DeviceId.deviceId(Dpid.uri(dpid))) + .withSelector(buildSelector()) + .withTreatment(buildTreatment()) + .withPriority(stat.getPriority()) + .makeTemporary(stat.getIdleTimeout()) + .withCookie(stat.getCookie().getValue()) + .forTable(stat.getTableId().getValue()) + .build(); + + return new DefaultFlowEntry(rule, FlowEntryState.ADDED, + stat.getDurationSec(), stat.getPacketCount().getValue(), + stat.getByteCount().getValue()); + case REMOVED: + rule = DefaultFlowRule.builder() + .forDevice(DeviceId.deviceId(Dpid.uri(dpid))) + .withSelector(buildSelector()) + .withPriority(removed.getPriority()) + .makeTemporary(removed.getIdleTimeout()) + .withCookie(removed.getCookie().getValue()) + .forTable(removed.getTableId().getValue()) + .build(); + + return new DefaultFlowEntry(rule, FlowEntryState.REMOVED, removed.getDurationSec(), + removed.getPacketCount().getValue(), removed.getByteCount().getValue()); + case MOD: + FlowEntryState flowState = state.length > 0 ? state[0] : FlowEntryState.FAILED; + rule = DefaultFlowRule.builder() + .forDevice(DeviceId.deviceId(Dpid.uri(dpid))) + .withSelector(buildSelector()) + .withTreatment(buildTreatment()) + .withPriority(flowMod.getPriority()) + .makeTemporary(flowMod.getIdleTimeout()) + .withCookie(flowMod.getCookie().getValue()) + .forTable(flowMod.getTableId().getValue()) + .build(); + + return new DefaultFlowEntry(rule, flowState, 0, 0, 0); + default: + log.error("Unknown flow type : {}", this.type); + return null; + } + + } + + private List getInstructions(OFFlowMod entry) { + switch (entry.getVersion()) { + case OF_10: + return Lists.newArrayList(OFFactoryVer13.INSTANCE.instructions() + .applyActions( + entry.getActions())); + case OF_11: + case OF_12: + case OF_13: + return entry.getInstructions(); + default: + log.warn("Unknown OF version {}", entry.getVersion()); + } + return Lists.newLinkedList(); + } + + private List getInstructions(OFFlowStatsEntry entry) { + switch (entry.getVersion()) { + case OF_10: + return Lists.newArrayList( + OFFactoryVer13.INSTANCE.instructions().applyActions(entry.getActions())); + case OF_11: + case OF_12: + case OF_13: + return entry.getInstructions(); + default: + log.warn("Unknown OF version {}", entry.getVersion()); + } + return Lists.newLinkedList(); + } + + private TrafficTreatment buildTreatment() { + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); + // If this is a drop rule + if (instructions.size() == 0) { + builder.drop(); + return builder.build(); + } + for (OFInstruction in : instructions) { + switch (in.getType()) { + case GOTO_TABLE: + builder.transition(((int) ((OFInstructionGotoTable) in) + .getTableId().getValue())); + break; + case WRITE_METADATA: + OFInstructionWriteMetadata m = (OFInstructionWriteMetadata) in; + builder.writeMetadata(m.getMetadata().getValue(), + m.getMetadataMask().getValue()); + break; + case WRITE_ACTIONS: + builder.deferred(); + buildActions(((OFInstructionWriteActions) in).getActions(), + builder); + break; + case APPLY_ACTIONS: + builder.immediate(); + buildActions(((OFInstructionApplyActions) in).getActions(), + builder); + break; + case CLEAR_ACTIONS: + builder.wipeDeferred(); + break; + case EXPERIMENTER: + break; + case METER: + break; + default: + log.warn("Unknown instructions type {}", in.getType()); + } + } + + return builder.build(); + } + + private TrafficTreatment.Builder buildActions(List actions, + TrafficTreatment.Builder builder) { + for (OFAction act : actions) { + switch (act.getType()) { + case OUTPUT: + OFActionOutput out = (OFActionOutput) act; + builder.setOutput( + PortNumber.portNumber(out.getPort().getPortNumber())); + break; + case SET_VLAN_VID: + OFActionSetVlanVid vlan = (OFActionSetVlanVid) act; + builder.setVlanId(VlanId.vlanId(vlan.getVlanVid().getVlan())); + break; + case SET_VLAN_PCP: + OFActionSetVlanPcp pcp = (OFActionSetVlanPcp) act; + builder.setVlanPcp(pcp.getVlanPcp().getValue()); + break; + case SET_DL_DST: + OFActionSetDlDst dldst = (OFActionSetDlDst) act; + builder.setEthDst( + MacAddress.valueOf(dldst.getDlAddr().getLong())); + break; + case SET_DL_SRC: + OFActionSetDlSrc dlsrc = (OFActionSetDlSrc) act; + builder.setEthSrc( + MacAddress.valueOf(dlsrc.getDlAddr().getLong())); + + break; + case SET_NW_DST: + OFActionSetNwDst nwdst = (OFActionSetNwDst) act; + IPv4Address di = nwdst.getNwAddr(); + builder.setIpDst(Ip4Address.valueOf(di.getInt())); + break; + case SET_NW_SRC: + OFActionSetNwSrc nwsrc = (OFActionSetNwSrc) act; + IPv4Address si = nwsrc.getNwAddr(); + builder.setIpSrc(Ip4Address.valueOf(si.getInt())); + break; + case EXPERIMENTER: + OFActionExperimenter exp = (OFActionExperimenter) act; + if (exp.getExperimenter() == 0x80005A06 || + exp.getExperimenter() == 0x748771) { + OFActionCircuit ct = (OFActionCircuit) exp; + short lambda = ((OFOxmOchSigidBasic) ct.getField()).getValue().getChannelNumber(); + builder.add(Instructions.modL0Lambda(Lambda.indexedLambda(lambda))); + } else { + log.warn("Unsupported OFActionExperimenter {}", exp.getExperimenter()); + } + break; + case SET_FIELD: + OFActionSetField setField = (OFActionSetField) act; + handleSetField(builder, setField.getField()); + break; + case POP_MPLS: + OFActionPopMpls popMpls = (OFActionPopMpls) act; + builder.popMpls((short) popMpls.getEthertype().getValue()); + break; + case PUSH_MPLS: + builder.pushMpls(); + break; + case COPY_TTL_IN: + builder.copyTtlIn(); + break; + case COPY_TTL_OUT: + builder.copyTtlOut(); + break; + case DEC_MPLS_TTL: + builder.decMplsTtl(); + break; + case DEC_NW_TTL: + builder.decNwTtl(); + break; + case GROUP: + OFActionGroup group = (OFActionGroup) act; + builder.group(new DefaultGroupId(group.getGroup().getGroupNumber())); + break; + case STRIP_VLAN: + case POP_VLAN: + builder.popVlan(); + break; + case PUSH_VLAN: + builder.pushVlan(); + break; + case SET_TP_DST: + case SET_TP_SRC: + case POP_PBB: + case PUSH_PBB: + case SET_MPLS_LABEL: + case SET_MPLS_TC: + case SET_MPLS_TTL: + case SET_NW_ECN: + case SET_NW_TOS: + case SET_NW_TTL: + case SET_QUEUE: + + case ENQUEUE: + default: + log.warn("Action type {} not yet implemented.", act.getType()); + } + } + return builder; + } + + + private void handleSetField(TrafficTreatment.Builder builder, OFOxm oxm) { + switch (oxm.getMatchField().id) { + case VLAN_PCP: + @SuppressWarnings("unchecked") + OFOxm vlanpcp = (OFOxm) oxm; + builder.setVlanPcp(vlanpcp.getValue().getValue()); + break; + case VLAN_VID: + @SuppressWarnings("unchecked") + OFOxm vlanvid = (OFOxm) oxm; + builder.setVlanId(VlanId.vlanId(vlanvid.getValue().getVlan())); + break; + case ETH_DST: + @SuppressWarnings("unchecked") + OFOxm ethdst = + (OFOxm) oxm; + builder.setEthDst(MacAddress.valueOf(ethdst.getValue().getLong())); + break; + case ETH_SRC: + @SuppressWarnings("unchecked") + OFOxm ethsrc = + (OFOxm) oxm; + builder.setEthSrc(MacAddress.valueOf(ethsrc.getValue().getLong())); + break; + case IPV4_DST: + @SuppressWarnings("unchecked") + OFOxm ip4dst = (OFOxm) oxm; + builder.setIpDst(Ip4Address.valueOf(ip4dst.getValue().getInt())); + break; + case IPV4_SRC: + @SuppressWarnings("unchecked") + OFOxm ip4src = (OFOxm) oxm; + builder.setIpSrc(Ip4Address.valueOf(ip4src.getValue().getInt())); + break; + case MPLS_LABEL: + @SuppressWarnings("unchecked") + OFOxm labelId = (OFOxm) oxm; + builder.setMpls(MplsLabel.mplsLabel((int) labelId.getValue().getValue())); + break; + case MPLS_BOS: + @SuppressWarnings("unchecked") + OFOxm mplsBos = (OFOxm) oxm; + builder.setMplsBos(mplsBos.getValue() == U8.ZERO ? false : true); + break; + case TUNNEL_ID: + @SuppressWarnings("unchecked") + OFOxm tunnelId = (OFOxm) oxm; + builder.setTunnelId(tunnelId.getValue().getValue()); + break; + case TCP_DST: + @SuppressWarnings("unchecked") + OFOxm tcpdst = (OFOxm) oxm; + builder.setTcpDst(TpPort.tpPort(tcpdst.getValue().getPort())); + break; + case TCP_SRC: + @SuppressWarnings("unchecked") + OFOxm tcpsrc = (OFOxm) oxm; + builder.setTcpSrc(TpPort.tpPort(tcpsrc.getValue().getPort())); + break; + case UDP_DST: + @SuppressWarnings("unchecked") + OFOxm udpdst = (OFOxm) oxm; + builder.setUdpDst(TpPort.tpPort(udpdst.getValue().getPort())); + break; + case UDP_SRC: + @SuppressWarnings("unchecked") + OFOxm udpsrc = (OFOxm) oxm; + builder.setUdpSrc(TpPort.tpPort(udpsrc.getValue().getPort())); + break; + case ARP_OP: + case ARP_SHA: + case ARP_SPA: + case ARP_THA: + case ARP_TPA: + case BSN_EGR_PORT_GROUP_ID: + case BSN_GLOBAL_VRF_ALLOWED: + case BSN_IN_PORTS_128: + case BSN_L3_DST_CLASS_ID: + case BSN_L3_INTERFACE_CLASS_ID: + case BSN_L3_SRC_CLASS_ID: + case BSN_LAG_ID: + case BSN_TCP_FLAGS: + case BSN_UDF0: + case BSN_UDF1: + case BSN_UDF2: + case BSN_UDF3: + case BSN_UDF4: + case BSN_UDF5: + case BSN_UDF6: + case BSN_UDF7: + case BSN_VLAN_XLATE_PORT_GROUP_ID: + case BSN_VRF: + case ETH_TYPE: + case ICMPV4_CODE: + case ICMPV4_TYPE: + case ICMPV6_CODE: + case ICMPV6_TYPE: + case IN_PHY_PORT: + case IN_PORT: + case IPV6_DST: + case IPV6_FLABEL: + case IPV6_ND_SLL: + case IPV6_ND_TARGET: + case IPV6_ND_TLL: + case IPV6_SRC: + case IP_DSCP: + case IP_ECN: + case IP_PROTO: + case METADATA: + case MPLS_TC: + case OCH_SIGID: + case OCH_SIGID_BASIC: + case OCH_SIGTYPE: + case OCH_SIGTYPE_BASIC: + case SCTP_DST: + case SCTP_SRC: + default: + log.warn("Set field type {} not yet implemented.", oxm.getMatchField().id); + break; + } + } + + // CHECKSTYLE IGNORE MethodLength FOR NEXT 1 LINES + private TrafficSelector buildSelector() { + MacAddress mac; + Ip4Prefix ip4Prefix; + Ip6Address ip6Address; + Ip6Prefix ip6Prefix; + + TrafficSelector.Builder builder = DefaultTrafficSelector.builder(); + for (MatchField field : match.getMatchFields()) { + switch (field.id) { + case IN_PORT: + builder.matchInPort(PortNumber + .portNumber(match.get(MatchField.IN_PORT).getPortNumber())); + break; + case IN_PHY_PORT: + builder.matchInPhyPort(PortNumber + .portNumber(match.get(MatchField.IN_PHY_PORT).getPortNumber())); + break; + case METADATA: + long metadata = + match.get(MatchField.METADATA).getValue().getValue(); + builder.matchMetadata(metadata); + break; + case ETH_DST: + mac = MacAddress.valueOf(match.get(MatchField.ETH_DST).getLong()); + builder.matchEthDst(mac); + break; + case ETH_SRC: + mac = MacAddress.valueOf(match.get(MatchField.ETH_SRC).getLong()); + builder.matchEthSrc(mac); + break; + case ETH_TYPE: + int ethType = match.get(MatchField.ETH_TYPE).getValue(); + if (ethType == EthType.VLAN_FRAME.getValue()) { + builder.matchVlanId(VlanId.ANY); + } else { + builder.matchEthType((short) ethType); + } + break; + case VLAN_VID: + VlanId vlanId = null; + if (match.isPartiallyMasked(MatchField.VLAN_VID)) { + Masked masked = match.getMasked(MatchField.VLAN_VID); + if (masked.getValue().equals(OFVlanVidMatch.PRESENT) + && masked.getMask().equals(OFVlanVidMatch.PRESENT)) { + vlanId = VlanId.ANY; + } + } else { + if (!match.get(MatchField.VLAN_VID).isPresentBitSet()) { + vlanId = VlanId.NONE; + } else { + vlanId = VlanId.vlanId(match.get(MatchField.VLAN_VID).getVlan()); + } + } + if (vlanId != null) { + builder.matchVlanId(vlanId); + } + break; + case VLAN_PCP: + byte vlanPcp = match.get(MatchField.VLAN_PCP).getValue(); + builder.matchVlanPcp(vlanPcp); + break; + case IP_DSCP: + byte ipDscp = match.get(MatchField.IP_DSCP).getDscpValue(); + builder.matchIPDscp(ipDscp); + break; + case IP_ECN: + byte ipEcn = match.get(MatchField.IP_ECN).getEcnValue(); + builder.matchIPEcn(ipEcn); + break; + case IP_PROTO: + short proto = match.get(MatchField.IP_PROTO).getIpProtocolNumber(); + builder.matchIPProtocol((byte) proto); + break; + case IPV4_SRC: + if (match.isPartiallyMasked(MatchField.IPV4_SRC)) { + Masked maskedIp = match.getMasked(MatchField.IPV4_SRC); + ip4Prefix = Ip4Prefix.valueOf( + maskedIp.getValue().getInt(), + maskedIp.getMask().asCidrMaskLength()); + } else { + ip4Prefix = Ip4Prefix.valueOf( + match.get(MatchField.IPV4_SRC).getInt(), + Ip4Prefix.MAX_MASK_LENGTH); + } + builder.matchIPSrc(ip4Prefix); + break; + case IPV4_DST: + if (match.isPartiallyMasked(MatchField.IPV4_DST)) { + Masked maskedIp = match.getMasked(MatchField.IPV4_DST); + ip4Prefix = Ip4Prefix.valueOf( + maskedIp.getValue().getInt(), + maskedIp.getMask().asCidrMaskLength()); + } else { + ip4Prefix = Ip4Prefix.valueOf( + match.get(MatchField.IPV4_DST).getInt(), + Ip4Prefix.MAX_MASK_LENGTH); + } + builder.matchIPDst(ip4Prefix); + break; + case TCP_SRC: + builder.matchTcpSrc(TpPort.tpPort(match.get(MatchField.TCP_SRC).getPort())); + break; + case TCP_DST: + builder.matchTcpDst(TpPort.tpPort(match.get(MatchField.TCP_DST).getPort())); + break; + case UDP_SRC: + builder.matchUdpSrc(TpPort.tpPort(match.get(MatchField.UDP_SRC).getPort())); + break; + case UDP_DST: + builder.matchUdpDst(TpPort.tpPort(match.get(MatchField.UDP_DST).getPort())); + break; + case MPLS_LABEL: + builder.matchMplsLabel(MplsLabel.mplsLabel((int) match.get(MatchField.MPLS_LABEL) + .getValue())); + break; + case MPLS_BOS: + builder.matchMplsBos(match.get(MatchField.MPLS_BOS).getValue()); + break; + case SCTP_SRC: + builder.matchSctpSrc(TpPort.tpPort(match.get(MatchField.SCTP_SRC).getPort())); + break; + case SCTP_DST: + builder.matchSctpDst(TpPort.tpPort(match.get(MatchField.SCTP_DST).getPort())); + break; + case ICMPV4_TYPE: + byte icmpType = (byte) match.get(MatchField.ICMPV4_TYPE).getType(); + builder.matchIcmpType(icmpType); + break; + case ICMPV4_CODE: + byte icmpCode = (byte) match.get(MatchField.ICMPV4_CODE).getCode(); + builder.matchIcmpCode(icmpCode); + break; + case IPV6_SRC: + if (match.isPartiallyMasked(MatchField.IPV6_SRC)) { + Masked maskedIp = match.getMasked(MatchField.IPV6_SRC); + ip6Prefix = Ip6Prefix.valueOf( + maskedIp.getValue().getBytes(), + maskedIp.getMask().asCidrMaskLength()); + } else { + ip6Prefix = Ip6Prefix.valueOf( + match.get(MatchField.IPV6_SRC).getBytes(), + Ip6Prefix.MAX_MASK_LENGTH); + } + builder.matchIPv6Src(ip6Prefix); + break; + case IPV6_DST: + if (match.isPartiallyMasked(MatchField.IPV6_DST)) { + Masked maskedIp = match.getMasked(MatchField.IPV6_DST); + ip6Prefix = Ip6Prefix.valueOf( + maskedIp.getValue().getBytes(), + maskedIp.getMask().asCidrMaskLength()); + } else { + ip6Prefix = Ip6Prefix.valueOf( + match.get(MatchField.IPV6_DST).getBytes(), + Ip6Prefix.MAX_MASK_LENGTH); + } + builder.matchIPv6Dst(ip6Prefix); + break; + case IPV6_FLABEL: + int flowLabel = + match.get(MatchField.IPV6_FLABEL).getIPv6FlowLabelValue(); + builder.matchIPv6FlowLabel(flowLabel); + break; + case ICMPV6_TYPE: + byte icmpv6type = (byte) match.get(MatchField.ICMPV6_TYPE).getValue(); + builder.matchIcmpv6Type(icmpv6type); + break; + case ICMPV6_CODE: + byte icmpv6code = (byte) match.get(MatchField.ICMPV6_CODE).getValue(); + builder.matchIcmpv6Code(icmpv6code); + break; + case IPV6_ND_TARGET: + ip6Address = + Ip6Address.valueOf(match.get(MatchField.IPV6_ND_TARGET).getBytes()); + builder.matchIPv6NDTargetAddress(ip6Address); + break; + case IPV6_ND_SLL: + mac = MacAddress.valueOf(match.get(MatchField.IPV6_ND_SLL).getLong()); + builder.matchIPv6NDSourceLinkLayerAddress(mac); + break; + case IPV6_ND_TLL: + mac = MacAddress.valueOf(match.get(MatchField.IPV6_ND_TLL).getLong()); + builder.matchIPv6NDTargetLinkLayerAddress(mac); + break; + case IPV6_EXTHDR: + builder.matchIPv6ExthdrFlags((short) match.get(MatchField.IPV6_EXTHDR) + .getValue()); + break; + case OCH_SIGID: + CircuitSignalID sigId = match.get(MatchField.OCH_SIGID); + builder.add(matchLambda(Lambda.ochSignal( + lookupGridType(sigId.getGridType()), lookupChannelSpacing(sigId.getChannelSpacing()), + sigId.getChannelNumber(), sigId.getSpectralWidth()) + )); + break; + case OCH_SIGTYPE: + U8 sigType = match.get(MatchField.OCH_SIGTYPE); + builder.add(matchOchSignalType(lookupOchSignalType((byte) sigType.getValue()))); + break; + case TUNNEL_ID: + long tunnelId = match.get(MatchField.TUNNEL_ID).getValue(); + builder.matchTunnelId(tunnelId); + break; + case ARP_OP: + case ARP_SHA: + case ARP_SPA: + case ARP_THA: + case ARP_TPA: + case MPLS_TC: + default: + log.warn("Match type {} not yet implemented.", field.id); + } + } + return builder.build(); + } +} diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java new file mode 100644 index 00000000..e050524a --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java @@ -0,0 +1,444 @@ +/* + * Copyright 2014-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.provider.of.flow.impl; + +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.Optional; + +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.Ip6Address; +import org.onlab.packet.Ip6Prefix; +import org.onlab.packet.VlanId; +import org.onosproject.net.OchSignal; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.criteria.Criterion; +import org.onosproject.net.flow.criteria.EthCriterion; +import org.onosproject.net.flow.criteria.EthTypeCriterion; +import org.onosproject.net.flow.criteria.IPCriterion; +import org.onosproject.net.flow.criteria.IPDscpCriterion; +import org.onosproject.net.flow.criteria.IPEcnCriterion; +import org.onosproject.net.flow.criteria.IPProtocolCriterion; +import org.onosproject.net.flow.criteria.IPv6ExthdrFlagsCriterion; +import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion; +import org.onosproject.net.flow.criteria.IPv6NDLinkLayerAddressCriterion; +import org.onosproject.net.flow.criteria.IPv6NDTargetAddressCriterion; +import org.onosproject.net.flow.criteria.IcmpCodeCriterion; +import org.onosproject.net.flow.criteria.IcmpTypeCriterion; +import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion; +import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion; +import org.onosproject.net.flow.criteria.MetadataCriterion; +import org.onosproject.net.flow.criteria.MplsBosCriterion; +import org.onosproject.net.flow.criteria.MplsCriterion; +import org.onosproject.net.flow.criteria.OchSignalCriterion; +import org.onosproject.net.flow.criteria.OchSignalTypeCriterion; +import org.onosproject.net.flow.criteria.PortCriterion; +import org.onosproject.net.flow.criteria.SctpPortCriterion; +import org.onosproject.net.flow.criteria.TcpPortCriterion; +import org.onosproject.net.flow.criteria.TunnelIdCriterion; +import org.onosproject.net.flow.criteria.UdpPortCriterion; +import org.onosproject.net.flow.criteria.VlanIdCriterion; +import org.onosproject.net.flow.criteria.VlanPcpCriterion; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFFlowAdd; +import org.projectfloodlight.openflow.protocol.OFFlowDelete; +import org.projectfloodlight.openflow.protocol.OFFlowMod; +import org.projectfloodlight.openflow.protocol.match.Match; +import org.projectfloodlight.openflow.protocol.match.MatchField; +import org.projectfloodlight.openflow.types.CircuitSignalID; +import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.ICMPv4Code; +import org.projectfloodlight.openflow.types.ICMPv4Type; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.IPv6FlowLabel; +import org.projectfloodlight.openflow.types.IpDscp; +import org.projectfloodlight.openflow.types.IpEcn; +import org.projectfloodlight.openflow.types.IpProtocol; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.Masked; +import org.projectfloodlight.openflow.types.OFBooleanValue; +import org.projectfloodlight.openflow.types.OFMetadata; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.TransportPort; +import org.projectfloodlight.openflow.types.U16; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U64; +import org.projectfloodlight.openflow.types.U8; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.projectfloodlight.openflow.types.VlanVid; +import org.slf4j.Logger; + +/** + * Builder for OpenFlow flow mods based on FlowRules. + */ +public abstract class FlowModBuilder { + + private final Logger log = getLogger(getClass()); + + private final OFFactory factory; + private final FlowRule flowRule; + private final TrafficSelector selector; + protected final Long xid; + + /** + * Creates a new flow mod builder. + * + * @param flowRule the flow rule to transform into a flow mod + * @param factory the OpenFlow factory to use to build the flow mod + * @param xid the transaction ID + * @return the new flow mod builder + */ + public static FlowModBuilder builder(FlowRule flowRule, + OFFactory factory, + Optional xid) { + switch (factory.getVersion()) { + case OF_10: + return new FlowModBuilderVer10(flowRule, factory, xid); + case OF_13: + return new FlowModBuilderVer13(flowRule, factory, xid); + default: + throw new UnsupportedOperationException( + "No flow mod builder for protocol version " + factory.getVersion()); + } + } + + /** + * Constructs a flow mod builder. + * + * @param flowRule the flow rule to transform into a flow mod + * @param factory the OpenFlow factory to use to build the flow mod + * @param xid the transaction ID + */ + protected FlowModBuilder(FlowRule flowRule, OFFactory factory, Optional xid) { + this.factory = factory; + this.flowRule = flowRule; + this.selector = flowRule.selector(); + this.xid = xid.orElse(0L); + + } + + /** + * Builds an ADD flow mod. + * + * @return the flow mod + */ + public abstract OFFlowAdd buildFlowAdd(); + + /** + * Builds a MODIFY flow mod. + * + * @return the flow mod + */ + public abstract OFFlowMod buildFlowMod(); + + /** + * Builds a DELETE flow mod. + * + * @return the flow mod + */ + public abstract OFFlowDelete buildFlowDel(); + + /** + * Builds the match for the flow mod. + * + * @return the match + */ + // CHECKSTYLE IGNORE MethodLength FOR NEXT 300 LINES + protected Match buildMatch() { + Match.Builder mBuilder = factory.buildMatch(); + Ip6Address ip6Address; + Ip4Prefix ip4Prefix; + Ip6Prefix ip6Prefix; + EthCriterion ethCriterion; + IPCriterion ipCriterion; + TcpPortCriterion tcpPortCriterion; + UdpPortCriterion udpPortCriterion; + SctpPortCriterion sctpPortCriterion; + IPv6NDLinkLayerAddressCriterion llAddressCriterion; + + for (Criterion c : selector.criteria()) { + switch (c.type()) { + case IN_PORT: + PortCriterion inPort = (PortCriterion) c; + mBuilder.setExact(MatchField.IN_PORT, + OFPort.of((int) inPort.port().toLong())); + break; + case IN_PHY_PORT: + PortCriterion inPhyPort = (PortCriterion) c; + mBuilder.setExact(MatchField.IN_PORT, + OFPort.of((int) inPhyPort.port().toLong())); + break; + case METADATA: + MetadataCriterion metadata = (MetadataCriterion) c; + mBuilder.setExact(MatchField.METADATA, + OFMetadata.ofRaw(metadata.metadata())); + break; + case ETH_DST: + ethCriterion = (EthCriterion) c; + mBuilder.setExact(MatchField.ETH_DST, + MacAddress.of(ethCriterion.mac().toLong())); + break; + case ETH_SRC: + ethCriterion = (EthCriterion) c; + mBuilder.setExact(MatchField.ETH_SRC, + MacAddress.of(ethCriterion.mac().toLong())); + break; + case ETH_TYPE: + EthTypeCriterion ethType = (EthTypeCriterion) c; + mBuilder.setExact(MatchField.ETH_TYPE, EthType.of(ethType.ethType().toShort())); + break; + case VLAN_VID: + VlanIdCriterion vid = (VlanIdCriterion) c; + + if (vid.vlanId().equals(VlanId.ANY)) { + mBuilder.setMasked(MatchField.VLAN_VID, OFVlanVidMatch.PRESENT, + OFVlanVidMatch.PRESENT); + } else if (vid.vlanId().equals(VlanId.NONE)) { + mBuilder.setExact(MatchField.VLAN_VID, OFVlanVidMatch.NONE); + } else { + mBuilder.setExact(MatchField.VLAN_VID, + OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(vid.vlanId().toShort()))); + } + break; + case VLAN_PCP: + VlanPcpCriterion vpcp = (VlanPcpCriterion) c; + mBuilder.setExact(MatchField.VLAN_PCP, VlanPcp.of(vpcp.priority())); + break; + case IP_DSCP: + IPDscpCriterion ipDscpCriterion = (IPDscpCriterion) c; + mBuilder.setExact(MatchField.IP_DSCP, + IpDscp.of(ipDscpCriterion.ipDscp())); + break; + case IP_ECN: + IPEcnCriterion ipEcnCriterion = (IPEcnCriterion) c; + mBuilder.setExact(MatchField.IP_ECN, + IpEcn.of(ipEcnCriterion.ipEcn())); + break; + case IP_PROTO: + IPProtocolCriterion p = (IPProtocolCriterion) c; + mBuilder.setExact(MatchField.IP_PROTO, IpProtocol.of(p.protocol())); + break; + case IPV4_SRC: + ipCriterion = (IPCriterion) c; + ip4Prefix = ipCriterion.ip().getIp4Prefix(); + if (ip4Prefix.prefixLength() != Ip4Prefix.MAX_MASK_LENGTH) { + Ip4Address maskAddr = + Ip4Address.makeMaskPrefix(ip4Prefix.prefixLength()); + Masked maskedIp = + Masked.of(IPv4Address.of(ip4Prefix.address().toInt()), + IPv4Address.of(maskAddr.toInt())); + mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp); + } else { + mBuilder.setExact(MatchField.IPV4_SRC, + IPv4Address.of(ip4Prefix.address().toInt())); + } + break; + case IPV4_DST: + ipCriterion = (IPCriterion) c; + ip4Prefix = ipCriterion.ip().getIp4Prefix(); + if (ip4Prefix.prefixLength() != Ip4Prefix.MAX_MASK_LENGTH) { + Ip4Address maskAddr = + Ip4Address.makeMaskPrefix(ip4Prefix.prefixLength()); + Masked maskedIp = + Masked.of(IPv4Address.of(ip4Prefix.address().toInt()), + IPv4Address.of(maskAddr.toInt())); + mBuilder.setMasked(MatchField.IPV4_DST, maskedIp); + } else { + mBuilder.setExact(MatchField.IPV4_DST, + IPv4Address.of(ip4Prefix.address().toInt())); + } + break; + case TCP_SRC: + tcpPortCriterion = (TcpPortCriterion) c; + mBuilder.setExact(MatchField.TCP_SRC, + TransportPort.of(tcpPortCriterion.tcpPort().toInt())); + break; + case TCP_DST: + tcpPortCriterion = (TcpPortCriterion) c; + mBuilder.setExact(MatchField.TCP_DST, + TransportPort.of(tcpPortCriterion.tcpPort().toInt())); + break; + case UDP_SRC: + udpPortCriterion = (UdpPortCriterion) c; + mBuilder.setExact(MatchField.UDP_SRC, + TransportPort.of(udpPortCriterion.udpPort().toInt())); + break; + case UDP_DST: + udpPortCriterion = (UdpPortCriterion) c; + mBuilder.setExact(MatchField.UDP_DST, + TransportPort.of(udpPortCriterion.udpPort().toInt())); + break; + case SCTP_SRC: + sctpPortCriterion = (SctpPortCriterion) c; + mBuilder.setExact(MatchField.SCTP_SRC, + TransportPort.of(sctpPortCriterion.sctpPort().toInt())); + break; + case SCTP_DST: + sctpPortCriterion = (SctpPortCriterion) c; + mBuilder.setExact(MatchField.SCTP_DST, + TransportPort.of(sctpPortCriterion.sctpPort().toInt())); + break; + case ICMPV4_TYPE: + IcmpTypeCriterion icmpType = (IcmpTypeCriterion) c; + mBuilder.setExact(MatchField.ICMPV4_TYPE, + ICMPv4Type.of(icmpType.icmpType())); + break; + case ICMPV4_CODE: + IcmpCodeCriterion icmpCode = (IcmpCodeCriterion) c; + mBuilder.setExact(MatchField.ICMPV4_CODE, + ICMPv4Code.of(icmpCode.icmpCode())); + break; + case IPV6_SRC: + ipCriterion = (IPCriterion) c; + ip6Prefix = ipCriterion.ip().getIp6Prefix(); + if (ip6Prefix.prefixLength() != Ip6Prefix.MAX_MASK_LENGTH) { + Ip6Address maskAddr = + Ip6Address.makeMaskPrefix(ip6Prefix.prefixLength()); + Masked maskedIp = + Masked.of(IPv6Address.of(ip6Prefix.address().toString()), + IPv6Address.of(maskAddr.toString())); + mBuilder.setMasked(MatchField.IPV6_SRC, maskedIp); + } else { + mBuilder.setExact(MatchField.IPV6_SRC, + IPv6Address.of(ip6Prefix.address().toString())); + } + break; + case IPV6_DST: + ipCriterion = (IPCriterion) c; + ip6Prefix = ipCriterion.ip().getIp6Prefix(); + if (ip6Prefix.prefixLength() != Ip6Prefix.MAX_MASK_LENGTH) { + Ip6Address maskAddr = + Ip6Address.makeMaskPrefix(ip6Prefix.prefixLength()); + Masked maskedIp = + Masked.of(IPv6Address.of(ip6Prefix.address().toString()), + IPv6Address.of(maskAddr.toString())); + mBuilder.setMasked(MatchField.IPV6_DST, maskedIp); + } else { + mBuilder.setExact(MatchField.IPV6_DST, + IPv6Address.of(ip6Prefix.address().toString())); + } + break; + case IPV6_FLABEL: + IPv6FlowLabelCriterion flowLabelCriterion = + (IPv6FlowLabelCriterion) c; + mBuilder.setExact(MatchField.IPV6_FLABEL, + IPv6FlowLabel.of(flowLabelCriterion.flowLabel())); + break; + case ICMPV6_TYPE: + Icmpv6TypeCriterion icmpv6Type = (Icmpv6TypeCriterion) c; + mBuilder.setExact(MatchField.ICMPV6_TYPE, + U8.of(icmpv6Type.icmpv6Type())); + break; + case ICMPV6_CODE: + Icmpv6CodeCriterion icmpv6Code = (Icmpv6CodeCriterion) c; + mBuilder.setExact(MatchField.ICMPV6_CODE, + U8.of(icmpv6Code.icmpv6Code())); + break; + case IPV6_ND_TARGET: + IPv6NDTargetAddressCriterion targetAddressCriterion = + (IPv6NDTargetAddressCriterion) c; + ip6Address = targetAddressCriterion.targetAddress(); + mBuilder.setExact(MatchField.IPV6_ND_TARGET, + IPv6Address.of(ip6Address.toOctets())); + break; + case IPV6_ND_SLL: + llAddressCriterion = + (IPv6NDLinkLayerAddressCriterion) c; + mBuilder.setExact(MatchField.IPV6_ND_SLL, + MacAddress.of(llAddressCriterion.mac().toLong())); + break; + case IPV6_ND_TLL: + llAddressCriterion = + (IPv6NDLinkLayerAddressCriterion) c; + mBuilder.setExact(MatchField.IPV6_ND_TLL, + MacAddress.of(llAddressCriterion.mac().toLong())); + break; + case MPLS_LABEL: + MplsCriterion mp = (MplsCriterion) c; + mBuilder.setExact(MatchField.MPLS_LABEL, U32.of(mp.label().toInt())); + break; + case IPV6_EXTHDR: + IPv6ExthdrFlagsCriterion exthdrFlagsCriterion = + (IPv6ExthdrFlagsCriterion) c; + mBuilder.setExact(MatchField.IPV6_EXTHDR, + U16.of(exthdrFlagsCriterion.exthdrFlags())); + break; + case OCH_SIGID: + try { + OchSignalCriterion ochSignalCriterion = (OchSignalCriterion) c; + OchSignal signal = ochSignalCriterion.lambda(); + byte gridType = OpenFlowValueMapper.lookupGridType(signal.gridType()); + byte channelSpacing = OpenFlowValueMapper.lookupChannelSpacing(signal.channelSpacing()); + mBuilder.setExact(MatchField.OCH_SIGID, + new CircuitSignalID(gridType, channelSpacing, + (short) signal.spacingMultiplier(), (short) signal.slotGranularity())); + } catch (NoMappingFoundException e) { + log.warn(e.getMessage()); + } + break; + case OCH_SIGTYPE: + OchSignalTypeCriterion sc = (OchSignalTypeCriterion) c; + byte signalType = OpenFlowValueMapper.lookupOchSignalType(sc.signalType()); + mBuilder.setExact(MatchField.OCH_SIGTYPE, U8.of(signalType)); + break; + case TUNNEL_ID: + TunnelIdCriterion tunnelId = (TunnelIdCriterion) c; + mBuilder.setExact(MatchField.TUNNEL_ID, + U64.of(tunnelId.tunnelId())); + break; + case MPLS_BOS: + MplsBosCriterion mplsBos = (MplsBosCriterion) c; + mBuilder.setExact(MatchField.MPLS_BOS, + mplsBos.mplsBos() ? OFBooleanValue.TRUE + : OFBooleanValue.FALSE); + break; + case ARP_OP: + case ARP_SHA: + case ARP_SPA: + case ARP_THA: + case ARP_TPA: + case MPLS_TC: + case PBB_ISID: + default: + log.warn("Match type {} not yet implemented.", c.type()); + } + } + return mBuilder.build(); + } + + /** + * Returns the flow rule for this builder. + * + * @return the flow rule + */ + protected FlowRule flowRule() { + return flowRule; + } + + /** + * Returns the factory used for building OpenFlow constructs. + * + * @return the factory + */ + protected OFFactory factory() { + return factory; + } + +} diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java new file mode 100644 index 00000000..c9de4500 --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java @@ -0,0 +1,230 @@ +/* + * Copyright 2014-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.provider.of.flow.impl; + +import org.onlab.packet.Ip4Address; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction; +import org.onosproject.net.flow.instructions.L3ModificationInstruction; +import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFFlowAdd; +import org.projectfloodlight.openflow.protocol.OFFlowDelete; +import org.projectfloodlight.openflow.protocol.OFFlowMod; +import org.projectfloodlight.openflow.protocol.OFFlowModFlags; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.match.Match; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.OFBufferId; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.U64; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.projectfloodlight.openflow.types.VlanVid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +/** + * Flow mod builder for OpenFlow 1.0. + */ +public class FlowModBuilderVer10 extends FlowModBuilder { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private static final int OFPCML_NO_BUFFER = 0xffff; + + private final TrafficTreatment treatment; + + /** + * Constructor for a flow mod builder for OpenFlow 1.0. + * + * @param flowRule the flow rule to transform into a flow mod + * @param factory the OpenFlow factory to use to build the flow mod + * @param xid the transaction ID + */ + protected FlowModBuilderVer10(FlowRule flowRule, + OFFactory factory, Optional xid) { + super(flowRule, factory, xid); + + this.treatment = flowRule.treatment(); + } + + @Override + public OFFlowAdd buildFlowAdd() { + Match match = buildMatch(); + List actions = buildActions(); + + long cookie = flowRule().id().value(); + + + OFFlowAdd fm = factory().buildFlowAdd() + .setXid(xid) + .setCookie(U64.of(cookie)) + .setBufferId(OFBufferId.NO_BUFFER) + .setActions(actions) + .setMatch(match) + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) + .setPriority(flowRule().priority()) + .build(); + + return fm; + } + + @Override + public OFFlowMod buildFlowMod() { + Match match = buildMatch(); + List actions = buildActions(); + + long cookie = flowRule().id().value(); + + OFFlowMod fm = factory().buildFlowModify() + .setXid(xid) + .setCookie(U64.of(cookie)) + .setBufferId(OFBufferId.NO_BUFFER) + .setActions(actions) + .setMatch(match) + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) + .setPriority(flowRule().priority()) + .build(); + + return fm; + } + + @Override + public OFFlowDelete buildFlowDel() { + Match match = buildMatch(); + + long cookie = flowRule().id().value(); + + OFFlowDelete fm = factory().buildFlowDelete() + .setXid(xid) + .setCookie(U64.of(cookie)) + .setBufferId(OFBufferId.NO_BUFFER) + .setMatch(match) + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) + .setPriority(flowRule().priority()) + .build(); + + return fm; + } + + private List buildActions() { + List acts = new LinkedList<>(); + OFAction act; + if (treatment == null) { + return acts; + } + for (Instruction i : treatment.immediate()) { + switch (i.type()) { + case DROP: + log.warn("Saw drop action; assigning drop action"); + return Collections.emptyList(); + case L2MODIFICATION: + act = buildL2Modification(i); + if (act != null) { + acts.add(buildL2Modification(i)); + } + break; + case L3MODIFICATION: + act = buildL3Modification(i); + if (act != null) { + acts.add(buildL3Modification(i)); + } + break; + case OUTPUT: + OutputInstruction out = (OutputInstruction) i; + OFActionOutput.Builder action = factory().actions().buildOutput() + .setPort(OFPort.of((int) out.port().toLong())); + if (out.port().equals(PortNumber.CONTROLLER)) { + action.setMaxLen(OFPCML_NO_BUFFER); + } + acts.add(action.build()); + break; + case L0MODIFICATION: + case GROUP: + case TABLE: + case METADATA: + log.warn("Instruction type {} not supported with protocol version {}", + i.type(), factory().getVersion()); + break; + default: + log.warn("Instruction type {} not yet implemented.", i.type()); + } + } + + return acts; + } + + private OFAction buildL3Modification(Instruction i) { + L3ModificationInstruction l3m = (L3ModificationInstruction) i; + ModIPInstruction ip; + Ip4Address ip4; + switch (l3m.subtype()) { + case IPV4_SRC: + ip = (ModIPInstruction) i; + ip4 = ip.ip().getIp4Address(); + return factory().actions().setNwSrc(IPv4Address.of(ip4.toInt())); + case IPV4_DST: + ip = (ModIPInstruction) i; + ip4 = ip.ip().getIp4Address(); + return factory().actions().setNwDst(IPv4Address.of(ip4.toInt())); + default: + log.warn("Unimplemented action type {}.", l3m.subtype()); + break; + } + return null; + } + + private OFAction buildL2Modification(Instruction i) { + L2ModificationInstruction l2m = (L2ModificationInstruction) i; + ModEtherInstruction eth; + switch (l2m.subtype()) { + case ETH_DST: + eth = (ModEtherInstruction) l2m; + return factory().actions().setDlDst(MacAddress.of(eth.mac().toLong())); + case ETH_SRC: + eth = (ModEtherInstruction) l2m; + return factory().actions().setDlSrc(MacAddress.of(eth.mac().toLong())); + case VLAN_ID: + ModVlanIdInstruction vlanId = (ModVlanIdInstruction) l2m; + return factory().actions().setVlanVid(VlanVid.ofVlan(vlanId.vlanId().toShort())); + case VLAN_PCP: + ModVlanPcpInstruction vlanPcp = (ModVlanPcpInstruction) l2m; + return factory().actions().setVlanPcp(VlanPcp.of(vlanPcp.vlanPcp())); + case VLAN_POP: + return factory().actions().stripVlan(); + case VLAN_PUSH: + return null; + default: + log.warn("Unimplemented action type {}.", l2m.subtype()); + break; + } + return null; + } + +} diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java new file mode 100644 index 00000000..8918d337 --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java @@ -0,0 +1,458 @@ +/* + * Copyright 2014-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.provider.of.flow.impl; + +import com.google.common.collect.Lists; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip6Address; +import org.onosproject.net.OchSignal; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.flow.instructions.Instructions.GroupInstruction; +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; +import org.onosproject.net.flow.instructions.L0ModificationInstruction; +import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModLambdaInstruction; +import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModOchSignalInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.PushHeaderInstructions; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction; +import org.onosproject.net.flow.instructions.L3ModificationInstruction; +import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction; +import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction; +import org.onosproject.net.flow.instructions.L4ModificationInstruction; +import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFFlowAdd; +import org.projectfloodlight.openflow.protocol.OFFlowDelete; +import org.projectfloodlight.openflow.protocol.OFFlowMod; +import org.projectfloodlight.openflow.protocol.OFFlowModFlags; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.action.OFActionGroup; +import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; +import org.projectfloodlight.openflow.protocol.match.Match; +import org.projectfloodlight.openflow.protocol.oxm.OFOxm; +import org.projectfloodlight.openflow.types.CircuitSignalID; +import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.IPv6FlowLabel; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.OFBooleanValue; +import org.projectfloodlight.openflow.types.OFBufferId; +import org.projectfloodlight.openflow.types.OFGroup; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.TableId; +import org.projectfloodlight.openflow.types.TransportPort; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U64; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +/** + * Flow mod builder for OpenFlow 1.3+. + */ +public class FlowModBuilderVer13 extends FlowModBuilder { + + private final Logger log = LoggerFactory.getLogger(getClass()); + private static final int OFPCML_NO_BUFFER = 0xffff; + + private final TrafficTreatment treatment; + + /** + * Constructor for a flow mod builder for OpenFlow 1.3. + * + * @param flowRule the flow rule to transform into a flow mod + * @param factory the OpenFlow factory to use to build the flow mod + * @param xid the transaction ID + */ + protected FlowModBuilderVer13(FlowRule flowRule, OFFactory factory, Optional xid) { + super(flowRule, factory, xid); + + this.treatment = flowRule.treatment(); + } + + @Override + public OFFlowAdd buildFlowAdd() { + Match match = buildMatch(); + List deferredActions = buildActions(treatment.deferred()); + List immediateActions = buildActions(treatment.immediate()); + List instructions = Lists.newLinkedList(); + + + if (treatment.clearedDeferred()) { + instructions.add(factory().instructions().clearActions()); + } + if (immediateActions.size() > 0) { + instructions.add(factory().instructions().applyActions(immediateActions)); + } + if (deferredActions.size() > 0) { + instructions.add(factory().instructions().writeActions(deferredActions)); + } + if (treatment.tableTransition() != null) { + instructions.add(buildTableGoto(treatment.tableTransition())); + } + if (treatment.writeMetadata() != null) { + instructions.add(buildMetadata(treatment.writeMetadata())); + } + + long cookie = flowRule().id().value(); + + OFFlowAdd fm = factory().buildFlowAdd() + .setXid(xid) + .setCookie(U64.of(cookie)) + .setBufferId(OFBufferId.NO_BUFFER) + .setInstructions(instructions) + .setMatch(match) + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) + .setPriority(flowRule().priority()) + .setTableId(TableId.of(flowRule().tableId())) + .build(); + + return fm; + } + + @Override + public OFFlowMod buildFlowMod() { + Match match = buildMatch(); + List deferredActions = buildActions(treatment.deferred()); + List immediateActions = buildActions(treatment.immediate()); + List instructions = Lists.newLinkedList(); + + + if (immediateActions.size() > 0) { + instructions.add(factory().instructions().applyActions(immediateActions)); + } + if (treatment.clearedDeferred()) { + instructions.add(factory().instructions().clearActions()); + } + if (deferredActions.size() > 0) { + instructions.add(factory().instructions().writeActions(deferredActions)); + } + if (treatment.tableTransition() != null) { + instructions.add(buildTableGoto(treatment.tableTransition())); + } + if (treatment.writeMetadata() != null) { + instructions.add(buildMetadata(treatment.writeMetadata())); + } + if (treatment.metered() != null) { + instructions.add(buildMeter(treatment.metered())); + } + + long cookie = flowRule().id().value(); + + OFFlowMod fm = factory().buildFlowModify() + .setXid(xid) + .setCookie(U64.of(cookie)) + .setBufferId(OFBufferId.NO_BUFFER) + .setInstructions(instructions) + .setMatch(match) + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) + .setPriority(flowRule().priority()) + .setTableId(TableId.of(flowRule().tableId())) + .build(); + + return fm; + } + + @Override + public OFFlowDelete buildFlowDel() { + Match match = buildMatch(); + + long cookie = flowRule().id().value(); + + OFFlowDelete fm = factory().buildFlowDelete() + .setXid(xid) + .setCookie(U64.of(cookie)) + .setBufferId(OFBufferId.NO_BUFFER) + .setMatch(match) + .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM)) + .setPriority(flowRule().priority()) + .setTableId(TableId.of(flowRule().tableId())) + .build(); + + return fm; + } + + private List buildActions(List treatments) { + if (treatment == null) { + return Collections.emptyList(); + } + + boolean tableFound = false; + List actions = new LinkedList<>(); + for (Instruction i : treatments) { + switch (i.type()) { + case DROP: + return Collections.emptyList(); + case L0MODIFICATION: + actions.add(buildL0Modification(i)); + break; + case L2MODIFICATION: + actions.add(buildL2Modification(i)); + break; + case L3MODIFICATION: + actions.add(buildL3Modification(i)); + break; + case L4MODIFICATION: + actions.add(buildL4Modification(i)); + break; + case OUTPUT: + OutputInstruction out = (OutputInstruction) i; + OFActionOutput.Builder action = factory().actions().buildOutput() + .setPort(OFPort.of((int) out.port().toLong())); + if (out.port().equals(PortNumber.CONTROLLER)) { + action.setMaxLen(OFPCML_NO_BUFFER); + } + actions.add(action.build()); + break; + case GROUP: + GroupInstruction group = (GroupInstruction) i; + OFActionGroup.Builder groupBuilder = factory().actions().buildGroup() + .setGroup(OFGroup.of(group.groupId().id())); + actions.add(groupBuilder.build()); + break; + case TABLE: + //FIXME: should not occur here. + tableFound = true; + break; + default: + log.warn("Instruction type {} not yet implemented.", i.type()); + } + } + if (tableFound && actions.isEmpty()) { + // handles the case where there are no actions, but there is + // a goto instruction for the next table + return Collections.emptyList(); + } + return actions; + } + + private OFInstruction buildTableGoto(Instructions.TableTypeTransition i) { + OFInstruction instruction = factory().instructions().gotoTable( + TableId.of(i.tableId())); + return instruction; + } + + private OFInstruction buildMetadata(Instructions.MetadataInstruction m) { + OFInstruction instruction = factory().instructions().writeMetadata( + U64.of(m.metadata()), U64.of(m.metadataMask())); + return instruction; + } + + private OFInstruction buildMeter(Instructions.MeterInstruction metered) { + return factory().instructions().meter(metered.meterId().id()); + } + + + private OFAction buildL0Modification(Instruction i) { + L0ModificationInstruction l0m = (L0ModificationInstruction) i; + switch (l0m.subtype()) { + case LAMBDA: + return buildModLambdaInstruction((ModLambdaInstruction) i); + case OCH: + try { + return buildModOchSignalInstruction((ModOchSignalInstruction) i); + } catch (NoMappingFoundException e) { + log.warn(e.getMessage()); + break; + } + default: + log.warn("Unimplemented action type {}.", l0m.subtype()); + break; + } + return null; + } + + private OFAction buildModLambdaInstruction(ModLambdaInstruction instruction) { + return factory().actions().circuit(factory().oxms().ochSigidBasic( + new CircuitSignalID((byte) 1, (byte) 2, instruction.lambda(), (short) 1))); + } + + private OFAction buildModOchSignalInstruction(ModOchSignalInstruction instruction) { + OchSignal signal = instruction.lambda(); + byte gridType = OpenFlowValueMapper.lookupGridType(signal.gridType()); + byte channelSpacing = OpenFlowValueMapper.lookupChannelSpacing(signal.channelSpacing()); + + return factory().actions().circuit(factory().oxms().ochSigidBasic( + new CircuitSignalID(gridType, channelSpacing, + (short) signal.spacingMultiplier(), (short) signal.slotGranularity()) + )); + } + + private OFAction buildL2Modification(Instruction i) { + L2ModificationInstruction l2m = (L2ModificationInstruction) i; + ModEtherInstruction eth; + OFOxm oxm = null; + switch (l2m.subtype()) { + case ETH_DST: + eth = (ModEtherInstruction) l2m; + oxm = factory().oxms().ethDst(MacAddress.of(eth.mac().toLong())); + break; + case ETH_SRC: + eth = (ModEtherInstruction) l2m; + oxm = factory().oxms().ethSrc(MacAddress.of(eth.mac().toLong())); + break; + case VLAN_ID: + ModVlanIdInstruction vlanId = (ModVlanIdInstruction) l2m; + oxm = factory().oxms().vlanVid(OFVlanVidMatch.ofVlan(vlanId.vlanId().toShort())); + break; + case VLAN_PCP: + ModVlanPcpInstruction vlanPcp = (ModVlanPcpInstruction) l2m; + oxm = factory().oxms().vlanPcp(VlanPcp.of(vlanPcp.vlanPcp())); + break; + case MPLS_PUSH: + PushHeaderInstructions pushHeaderInstructions = + (PushHeaderInstructions) l2m; + return factory().actions().pushMpls(EthType.of(pushHeaderInstructions + .ethernetType().toShort())); + case MPLS_POP: + PushHeaderInstructions popHeaderInstructions = + (PushHeaderInstructions) l2m; + return factory().actions().popMpls(EthType.of(popHeaderInstructions + .ethernetType().toShort())); + case MPLS_LABEL: + ModMplsLabelInstruction mplsLabel = + (ModMplsLabelInstruction) l2m; + oxm = factory().oxms().mplsLabel(U32.of(mplsLabel.mplsLabel().toInt())); + break; + case MPLS_BOS: + ModMplsBosInstruction mplsBos = (ModMplsBosInstruction) l2m; + oxm = factory().oxms() + .mplsBos(mplsBos.mplsBos() ? OFBooleanValue.TRUE + : OFBooleanValue.FALSE); + break; + case DEC_MPLS_TTL: + return factory().actions().decMplsTtl(); + case VLAN_POP: + return factory().actions().popVlan(); + case VLAN_PUSH: + PushHeaderInstructions pushVlanInstruction = (PushHeaderInstructions) l2m; + return factory().actions().pushVlan( + EthType.of(pushVlanInstruction.ethernetType().toShort())); + case TUNNEL_ID: + ModTunnelIdInstruction tunnelId = (ModTunnelIdInstruction) l2m; + oxm = factory().oxms().tunnelId(U64.of(tunnelId.tunnelId())); + break; + default: + log.warn("Unimplemented action type {}.", l2m.subtype()); + break; + } + + if (oxm != null) { + return factory().actions().buildSetField().setField(oxm).build(); + } + return null; + } + + private OFAction buildL3Modification(Instruction i) { + L3ModificationInstruction l3m = (L3ModificationInstruction) i; + ModIPInstruction ip; + Ip4Address ip4; + Ip6Address ip6; + OFOxm oxm = null; + switch (l3m.subtype()) { + case IPV4_SRC: + ip = (ModIPInstruction) i; + ip4 = ip.ip().getIp4Address(); + oxm = factory().oxms().ipv4Src(IPv4Address.of(ip4.toInt())); + break; + case IPV4_DST: + ip = (ModIPInstruction) i; + ip4 = ip.ip().getIp4Address(); + oxm = factory().oxms().ipv4Dst(IPv4Address.of(ip4.toInt())); + break; + case IPV6_SRC: + ip = (ModIPInstruction) i; + ip6 = ip.ip().getIp6Address(); + oxm = factory().oxms().ipv6Src(IPv6Address.of(ip6.toOctets())); + break; + case IPV6_DST: + ip = (ModIPInstruction) i; + ip6 = ip.ip().getIp6Address(); + oxm = factory().oxms().ipv6Dst(IPv6Address.of(ip6.toOctets())); + break; + case IPV6_FLABEL: + ModIPv6FlowLabelInstruction flowLabelInstruction = + (ModIPv6FlowLabelInstruction) i; + int flowLabel = flowLabelInstruction.flowLabel(); + oxm = factory().oxms().ipv6Flabel(IPv6FlowLabel.of(flowLabel)); + break; + case DEC_TTL: + return factory().actions().decNwTtl(); + case TTL_IN: + return factory().actions().copyTtlIn(); + case TTL_OUT: + return factory().actions().copyTtlOut(); + default: + log.warn("Unimplemented action type {}.", l3m.subtype()); + break; + } + + if (oxm != null) { + return factory().actions().buildSetField().setField(oxm).build(); + } + return null; + } + + private OFAction buildL4Modification(Instruction i) { + L4ModificationInstruction l4m = (L4ModificationInstruction) i; + ModTransportPortInstruction tp; + OFOxm oxm = null; + switch (l4m.subtype()) { + case TCP_SRC: + tp = (ModTransportPortInstruction) l4m; + oxm = factory().oxms().tcpSrc(TransportPort.of(tp.port().toInt())); + break; + case TCP_DST: + tp = (ModTransportPortInstruction) l4m; + oxm = factory().oxms().tcpDst(TransportPort.of(tp.port().toInt())); + break; + case UDP_SRC: + tp = (ModTransportPortInstruction) l4m; + oxm = factory().oxms().udpSrc(TransportPort.of(tp.port().toInt())); + break; + case UDP_DST: + tp = (ModTransportPortInstruction) l4m; + oxm = factory().oxms().udpDst(TransportPort.of(tp.port().toInt())); + break; + default: + log.warn("Unimplemented action type {}.", l4m.subtype()); + break; + } + + if (oxm != null) { + return factory().actions().buildSetField().setField(oxm).build(); + } + return null; + } + +} diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java new file mode 100644 index 00000000..c4c81afa --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java @@ -0,0 +1,100 @@ +/* + * Copyright 2014 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.provider.of.flow.impl; + +import org.onlab.util.SharedExecutors; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.TableId; +import org.slf4j.Logger; + +import java.util.Timer; +import java.util.TimerTask; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Collects flow statistics for the specified switch. + */ +class FlowStatsCollector { + + private final Logger log = getLogger(getClass()); + + public static final int SECONDS = 1000; + + private final OpenFlowSwitch sw; + private Timer timer; + private TimerTask task; + + private int pollInterval; + + /** + * Creates a new collector for the given switch and poll frequency. + * + * @param timer timer to use for scheduling + * @param sw switch to pull + * @param pollInterval poll frequency in seconds + */ + FlowStatsCollector(Timer timer, OpenFlowSwitch sw, int pollInterval) { + this.timer = timer; + this.sw = sw; + this.pollInterval = pollInterval; + } + + /** + * Adjusts poll frequency. + * + * @param pollInterval poll frequency in seconds + */ + synchronized void adjustPollInterval(int pollInterval) { + this.pollInterval = pollInterval; + task.cancel(); + task = new InternalTimerTask(); + timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000); + } + + private class InternalTimerTask extends TimerTask { + @Override + public void run() { + if (sw.getRole() == RoleState.MASTER) { + log.trace("Collecting stats for {}", sw.getStringId()); + OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest() + .setMatch(sw.factory().matchWildcardAll()) + .setTableId(TableId.ALL) + .setOutPort(OFPort.NO_MASK) + .build(); + sw.sendMsg(request); + } + } + } + + public synchronized void start() { + // Initially start polling quickly. Then drop down to configured value + log.debug("Starting Stats collection thread for {}", sw.getStringId()); + task = new InternalTimerTask(); + SharedExecutors.getTimer().scheduleAtFixedRate(task, 1 * SECONDS, + pollInterval * SECONDS); + } + + public synchronized void stop() { + log.debug("Stopping Stats collection thread for {}", sw.getStringId()); + task.cancel(); + task = null; + } + +} diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NoMappingFoundException.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NoMappingFoundException.java new file mode 100644 index 00000000..898b286d --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NoMappingFoundException.java @@ -0,0 +1,31 @@ +/* + * 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.provider.of.flow.impl; + +/** + * Thrown to indicate that no mapping for the input value is found. + */ +public class NoMappingFoundException extends RuntimeException { + /** + * Creates an instance with the specified values. + * + * @param input input value of mapping causing this exception + * @param output the desired class which the input value is mapped to + */ + public NoMappingFoundException(Object input, Class output) { + super(String.format("No mapping found for %s when converting to %s", input, output.getName())); + } +} diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java new file mode 100644 index 00000000..de079e03 --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java @@ -0,0 +1,453 @@ +/* + * Copyright 2014 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.provider.of.flow.impl; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalCause; +import com.google.common.cache.RemovalNotification; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Modified; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onosproject.cfg.ComponentConfigService; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.flow.CompletedBatchOperation; +import org.onosproject.net.flow.FlowEntry; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleBatchEntry; +import org.onosproject.net.flow.FlowRuleBatchOperation; +import org.onosproject.net.flow.FlowRuleExtPayLoad; +import org.onosproject.net.flow.FlowRuleProvider; +import org.onosproject.net.flow.FlowRuleProviderRegistry; +import org.onosproject.net.flow.FlowRuleProviderService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.net.statistic.DefaultLoad; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowEventListener; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitchListener; +import org.onosproject.openflow.controller.RoleState; +import org.onosproject.openflow.controller.ThirdPartyMessage; +import org.osgi.service.component.ComponentContext; +import org.projectfloodlight.openflow.protocol.OFBadRequestCode; +import org.projectfloodlight.openflow.protocol.OFBarrierRequest; +import org.projectfloodlight.openflow.protocol.OFErrorMsg; +import org.projectfloodlight.openflow.protocol.OFErrorType; +import org.projectfloodlight.openflow.protocol.OFFlowMod; +import org.projectfloodlight.openflow.protocol.OFFlowRemoved; +import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortStatus; +import org.projectfloodlight.openflow.protocol.OFStatsReply; +import org.projectfloodlight.openflow.protocol.OFStatsType; +import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg; +import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg; +import org.slf4j.Logger; + +import java.util.Collections; +import java.util.Dictionary; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.Timer; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.onlab.util.Tools.get; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Provider which uses an OpenFlow controller to detect network end-station + * hosts. + */ +@Component(immediate = true) +public class OpenFlowRuleProvider extends AbstractProvider + implements FlowRuleProvider { + + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowRuleProviderRegistry providerRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenFlowController controller; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ComponentConfigService cfgService; + + private static final int DEFAULT_POLL_FREQUENCY = 10; + @Property(name = "flowPollFrequency", intValue = DEFAULT_POLL_FREQUENCY, + label = "Frequency (in seconds) for polling flow statistics") + private int flowPollFrequency = DEFAULT_POLL_FREQUENCY; + + private FlowRuleProviderService providerService; + + private final InternalFlowProvider listener = new InternalFlowProvider(); + + private Cache pendingBatches; + + private final Timer timer = new Timer("onos-openflow-collector"); + private final Map collectors = Maps.newHashMap(); + + /** + * Creates an OpenFlow host provider. + */ + public OpenFlowRuleProvider() { + super(new ProviderId("of", "org.onosproject.provider.openflow")); + } + + @Activate + public void activate(ComponentContext context) { + cfgService.registerProperties(getClass()); + providerService = providerRegistry.register(this); + controller.addListener(listener); + controller.addEventListener(listener); + + pendingBatches = createBatchCache(); + createCollectors(); + + log.info("Started"); + } + + @Deactivate + public void deactivate(ComponentContext context) { + cfgService.unregisterProperties(getClass(), false); + stopCollectors(); + providerRegistry.unregister(this); + providerService = null; + + log.info("Stopped"); + } + + @Modified + public void modified(ComponentContext context) { + Dictionary properties = context.getProperties(); + int newFlowPollFrequency; + try { + String s = get(properties, "flowPollFrequency"); + newFlowPollFrequency = isNullOrEmpty(s) ? flowPollFrequency : Integer.parseInt(s.trim()); + + } catch (NumberFormatException | ClassCastException e) { + newFlowPollFrequency = flowPollFrequency; + } + + if (newFlowPollFrequency != flowPollFrequency) { + flowPollFrequency = newFlowPollFrequency; + adjustRate(); + } + + log.info("Settings: flowPollFrequency={}", flowPollFrequency); + } + + private Cache createBatchCache() { + return CacheBuilder.newBuilder() + .expireAfterWrite(10, TimeUnit.SECONDS) + .removalListener((RemovalNotification notification) -> { + if (notification.getCause() == RemovalCause.EXPIRED) { + providerService.batchOperationCompleted(notification.getKey(), + notification.getValue().failedCompletion()); + } + }).build(); + } + + private void createCollectors() { + controller.getSwitches().forEach(this::createCollector); + } + + private void createCollector(OpenFlowSwitch sw) { + FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency); + fsc.start(); + collectors.put(new Dpid(sw.getId()), fsc); + } + + private void stopCollectors() { + collectors.values().forEach(FlowStatsCollector::stop); + collectors.clear(); + } + + private void adjustRate() { + DefaultLoad.setPollInterval(flowPollFrequency); + collectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency)); + } + + @Override + public void applyFlowRule(FlowRule... flowRules) { + for (FlowRule flowRule : flowRules) { + applyRule(flowRule); + } + } + + private void applyRule(FlowRule flowRule) { + OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId() + .uri())); + FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad(); + if (hasPayload(flowRuleExtPayLoad)) { + OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad()); + sw.sendMsg(msg); + return; + } + sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(), + Optional.empty()).buildFlowAdd()); + } + + @Override + public void removeFlowRule(FlowRule... flowRules) { + for (FlowRule flowRule : flowRules) { + removeRule(flowRule); + } + } + + private void removeRule(FlowRule flowRule) { + OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId() + .uri())); + FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad(); + if (hasPayload(flowRuleExtPayLoad)) { + OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad()); + sw.sendMsg(msg); + return; + } + sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(), + Optional.empty()).buildFlowDel()); + } + + @Override + public void removeRulesById(ApplicationId id, FlowRule... flowRules) { + // TODO: optimize using the ApplicationId + removeFlowRule(flowRules); + } + + @Override + public void executeBatch(FlowRuleBatchOperation batch) { + + pendingBatches.put(batch.id(), new InternalCacheEntry(batch)); + + OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(batch.deviceId() + .uri())); + OFFlowMod mod; + for (FlowRuleBatchEntry fbe : batch.getOperations()) { + // flow is the third party privacy flow + + FlowRuleExtPayLoad flowRuleExtPayLoad = fbe.target().payLoad(); + if (hasPayload(flowRuleExtPayLoad)) { + OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad()); + sw.sendMsg(msg); + continue; + } + FlowModBuilder builder = FlowModBuilder.builder(fbe.target(), sw + .factory(), Optional.of(batch.id())); + switch (fbe.operator()) { + case ADD: + mod = builder.buildFlowAdd(); + break; + case REMOVE: + mod = builder.buildFlowDel(); + break; + case MODIFY: + mod = builder.buildFlowMod(); + break; + default: + log.error("Unsupported batch operation {}; skipping flowmod {}", + fbe.operator(), fbe); + continue; + } + sw.sendMsg(mod); + } + OFBarrierRequest.Builder builder = sw.factory().buildBarrierRequest() + .setXid(batch.id()); + sw.sendMsg(builder.build()); + } + + private boolean hasPayload(FlowRuleExtPayLoad flowRuleExtPayLoad) { + return flowRuleExtPayLoad != null && + flowRuleExtPayLoad.payLoad() != null && + flowRuleExtPayLoad.payLoad().length > 0; + } + + private class InternalFlowProvider + implements OpenFlowSwitchListener, OpenFlowEventListener { + + @Override + public void switchAdded(Dpid dpid) { + createCollector(controller.getSwitch(dpid)); + } + + @Override + public void switchRemoved(Dpid dpid) { + FlowStatsCollector collector = collectors.remove(dpid); + if (collector != null) { + collector.stop(); + } + } + + @Override + public void switchChanged(Dpid dpid) { + } + + @Override + public void portChanged(Dpid dpid, OFPortStatus status) { + // TODO: Decide whether to evict flows internal store. + } + + @Override + public void handleMessage(Dpid dpid, OFMessage msg) { + OpenFlowSwitch sw = controller.getSwitch(dpid); + switch (msg.getType()) { + case FLOW_REMOVED: + OFFlowRemoved removed = (OFFlowRemoved) msg; + + FlowEntry fr = new FlowEntryBuilder(dpid, removed).build(); + providerService.flowRemoved(fr); + break; + case STATS_REPLY: + if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) { + pushFlowMetrics(dpid, (OFFlowStatsReply) msg); + } + break; + case BARRIER_REPLY: + try { + InternalCacheEntry entry = pendingBatches.getIfPresent(msg.getXid()); + if (entry != null) { + providerService + .batchOperationCompleted(msg.getXid(), + entry.completed()); + } else { + log.warn("Received unknown Barrier Reply: {}", + msg.getXid()); + } + } finally { + pendingBatches.invalidate(msg.getXid()); + } + break; + case ERROR: + // TODO: This needs to get suppressed in a better way. + if (msg instanceof OFBadRequestErrorMsg && + ((OFBadRequestErrorMsg) msg).getCode() == OFBadRequestCode.BAD_TYPE) { + log.debug("Received error message {} from {}", msg, dpid); + } else { + log.warn("Received error message {} from {}", msg, dpid); + } + + OFErrorMsg error = (OFErrorMsg) msg; + if (error.getErrType() == OFErrorType.FLOW_MOD_FAILED) { + OFFlowModFailedErrorMsg fmFailed = (OFFlowModFailedErrorMsg) error; + if (fmFailed.getData().getParsedMessage().isPresent()) { + OFMessage m = fmFailed.getData().getParsedMessage().get(); + OFFlowMod fm = (OFFlowMod) m; + InternalCacheEntry entry = + pendingBatches.getIfPresent(msg.getXid()); + if (entry != null) { + entry.appendFailure(new FlowEntryBuilder(dpid, fm).build()); + } else { + log.error("No matching batch for this error: {}", error); + } + } else { + // FIXME: Potentially add flowtracking to avoid this message. + log.error("Flow installation failed but switch didn't" + + " tell us which one."); + } + } + break; + default: + log.debug("Unhandled message type: {}", msg.getType()); + } + + } + + @Override + public void receivedRoleReply(Dpid dpid, RoleState requested, + RoleState response) { + // Do nothing here for now. + } + + private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) { + + DeviceId did = DeviceId.deviceId(Dpid.uri(dpid)); + OpenFlowSwitch sw = controller.getSwitch(dpid); + + List flowEntries = replies.getEntries().stream() + .map(entry -> new FlowEntryBuilder(dpid, entry).build()) + .collect(Collectors.toList()); + + providerService.pushFlowMetrics(did, flowEntries); + } + } + + /** + * The internal cache entry holding the original request as well as + * accumulating the any failures along the way. + *

+ * If this entry is evicted from the cache then the entire operation is + * considered failed. Otherwise, only the failures reported by the device + * will be propagated up. + */ + private class InternalCacheEntry { + + private final FlowRuleBatchOperation operation; + private final Set failures = Sets.newConcurrentHashSet(); + + public InternalCacheEntry(FlowRuleBatchOperation operation) { + this.operation = operation; + } + + /** + * Appends a failed rule to the set of failed items. + * + * @param rule the failed rule + */ + public void appendFailure(FlowRule rule) { + failures.add(rule); + } + + /** + * Fails the entire batch and returns the failed operation. + * + * @return the failed operation + */ + public CompletedBatchOperation failedCompletion() { + Set fails = operation.getOperations().stream() + .map(op -> op.target()).collect(Collectors.toSet()); + return new CompletedBatchOperation(false, + Collections + .unmodifiableSet(fails), + operation.deviceId()); + } + + /** + * Returns the completed operation and whether the batch suceeded. + * + * @return the completed operation + */ + public CompletedBatchOperation completed() { + return new CompletedBatchOperation( + failures.isEmpty(), + Collections + .unmodifiableSet(failures), + operation.deviceId()); + } + } + +} diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowValueMapper.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowValueMapper.java new file mode 100644 index 00000000..2f0831c6 --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowValueMapper.java @@ -0,0 +1,152 @@ +/* + * 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.provider.of.flow.impl; + +import com.google.common.collect.BiMap; +import com.google.common.collect.EnumHashBiMap; +import org.onosproject.net.ChannelSpacing; +import org.onosproject.net.GridType; +import org.onosproject.net.OchSignalType; + +/** + * Collection of helper methods to convert protocol agnostic models to values used in OpenFlow spec. + */ +final class OpenFlowValueMapper { + + // prohibit instantiation + private OpenFlowValueMapper() {} + + private static final BiMap GRID_TYPES = EnumHashBiMap.create(GridType.class); + static { + // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values + GRID_TYPES.put(GridType.DWDM, (byte) 1); // OFPGRIDT_DWDM of enum ofp_grid_type + GRID_TYPES.put(GridType.CWDM, (byte) 2); // OFPGRIDT_CWDM of enum ofp_grid_type + GRID_TYPES.put(GridType.FLEX, (byte) 3); // OFPGRIDT_FLEX of enum ofp_grid_type + } + + private static final BiMap CHANNEL_SPACING = EnumHashBiMap.create(ChannelSpacing.class); + static { + // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values + CHANNEL_SPACING.put(ChannelSpacing.CHL_100GHZ, (byte) 1); // OFPCS_100GHZ of enum ofp_chl_spacing + CHANNEL_SPACING.put(ChannelSpacing.CHL_50GHZ, (byte) 2); // OFPCS_50GHZ of enum ofp_chl_spacing + CHANNEL_SPACING.put(ChannelSpacing.CHL_25GHZ, (byte) 3); // OFPCS_25GHZ of enum ofp_chl_spacing + CHANNEL_SPACING.put(ChannelSpacing.CHL_12P5GHZ, (byte) 4); // OFPCS_12P5GHZ of enum ofp_chl_spacing + CHANNEL_SPACING.put(ChannelSpacing.CHL_6P25GHZ, (byte) 5); // OFPCS_6P25GHZ of enum ofp_chl_spacing + } + + private static final BiMap OCH_SIGNAL_TYPES = EnumHashBiMap.create(OchSignalType.class); + static { + // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values + OCH_SIGNAL_TYPES.put(OchSignalType.FIXED_GRID, (byte) 1); // OFPOCHT_FIX_GRID of enum ofp_och_signal_type + OCH_SIGNAL_TYPES.put(OchSignalType.FLEX_GRID, (byte) 2); // OFPOCHT_FLEX_GRID of enum ofp_och_signal_type + } + + /** + * Looks up the specified input value to the corresponding value with the specified map. + * + * @param map bidirectional mapping + * @param input input value + * @param cls class of output value + * @param type of input value + * @param type of output value + * @return the corresponding value stored in the specified map + * @throws NoMappingFoundException if no corresponding value is found + */ + private static O lookup(BiMap map, I input, Class cls) { + if (!map.containsKey(input)) { + throw new NoMappingFoundException(input, cls); + } + + return map.get(input); + } + + /** + * Looks up the corresponding byte value defined in + * ONF "Optical Transport Protocol Extensions Version 1.0" + * from the specified {@link GridType} instance. + * + * @param type grid type + * @return the byte value corresponding to the specified grid type + * @throws NoMappingFoundException if the specified grid type is not found + */ + static byte lookupGridType(GridType type) { + return lookup(GRID_TYPES, type, Byte.class); + } + + /** + * Looks up the corresponding {@link GridType} instance + * from the specified byte value for grid type + * defined in ONF "Optical Transport Protocol Extensions Version 1.0". + * + * @param type byte value as grid type defined the spec + * @return the corresponding GridType instance + */ + static GridType lookupGridType(byte type) { + return lookup(GRID_TYPES.inverse(), type, GridType.class); + } + + /** + * Looks up the corresponding byte value for channel spacing defined in + * ONF "Optical Transport Protocol Extensions Version 1.0" + * from the specified {@link ChannelSpacing} instance. + * + * @param spacing channel spacing + * @return byte value corresponding to the specified channel spacing + * @throws NoMappingFoundException if the specified channel spacing is not found + */ + static byte lookupChannelSpacing(ChannelSpacing spacing) { + return lookup(CHANNEL_SPACING, spacing, Byte.class); + } + + /** + * Looks up the corresponding {@link ChannelSpacing} instance + * from the specified byte value for channel spacing + * defined in ONF "Optical Transport Protocol Extensions Version 1.0". + * + * @param spacing byte value as channel spacing defined the spec + * @return the corresponding ChannelSpacing instance + * @throws NoMappingFoundException if the specified channel spacing is not found + */ + static ChannelSpacing lookupChannelSpacing(byte spacing) { + return lookup(CHANNEL_SPACING.inverse(), spacing, ChannelSpacing.class); + } + + /** + * Looks up the corresponding byte value for Och signal type defined in + * ONF "Optical Transport Protocol Extensions Version 1.0" + * from the specified {@link OchSignalType} instance. + * + * @param signalType optical signal type + * @return byte value corresponding to the specified OCh signal type + * @throws NoMappingFoundException if the specified Och signal type is not found + */ + static byte lookupOchSignalType(OchSignalType signalType) { + return lookup(OCH_SIGNAL_TYPES, signalType, Byte.class); + } + + /** + * Looks up the the corresponding {@link OchSignalType} instance + * from the specified byte value for Och signal type defined in + * ONF "Optical Transport Protocol Extensions Version 1.0". + * + * @param signalType byte value as Och singal type defined the spec + * @return the corresponding OchSignalType instance + * @throws NoMappingFoundException if the specified Och signal type is not found + */ + static OchSignalType lookupOchSignalType(byte signalType) { + return lookup(OCH_SIGNAL_TYPES.inverse(), signalType, OchSignalType.class); + } +} diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/package-info.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/package-info.java new file mode 100644 index 00000000..2acc1510 --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2014 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. + */ + +/** + * Provider that uses OpenFlow controller as a means of ending and receiving flow information. + */ +package org.onosproject.provider.of.flow.impl; diff --git a/framework/src/onos/providers/openflow/group/pom.xml b/framework/src/onos/providers/openflow/group/pom.xml new file mode 100644 index 00000000..97ac5ffe --- /dev/null +++ b/framework/src/onos/providers/openflow/group/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + + org.onosproject + onos-of-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-of-provider-group + bundle + + ONOS OpenFlow protocol group provider + + \ No newline at end of file diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java new file mode 100644 index 00000000..b9de7c0f --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java @@ -0,0 +1,343 @@ +/* + * 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.provider.of.group.impl; + +import com.google.common.collect.Lists; + +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; +import org.onlab.packet.MplsLabel; +import org.onlab.packet.VlanId; +import org.onosproject.core.DefaultGroupId; +import org.onosproject.core.GroupId; +import org.onosproject.net.Lambda; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.group.DefaultGroupBucket; +import org.onosproject.net.group.GroupBucket; +import org.onosproject.net.group.GroupBuckets; +import org.projectfloodlight.openflow.protocol.OFBucket; +import org.projectfloodlight.openflow.protocol.OFGroupType; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.action.OFActionCircuit; +import org.projectfloodlight.openflow.protocol.action.OFActionCopyTtlIn; +import org.projectfloodlight.openflow.protocol.action.OFActionCopyTtlOut; +import org.projectfloodlight.openflow.protocol.action.OFActionDecMplsTtl; +import org.projectfloodlight.openflow.protocol.action.OFActionDecNwTtl; +import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter; +import org.projectfloodlight.openflow.protocol.action.OFActionGroup; +import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls; +import org.projectfloodlight.openflow.protocol.action.OFActionPushMpls; +import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst; +import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc; +import org.projectfloodlight.openflow.protocol.action.OFActionSetField; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst; +import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc; +import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp; +import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid; +import org.projectfloodlight.openflow.protocol.oxm.OFOxm; +import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.U8; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.slf4j.Logger; + +import java.util.List; + +import static org.slf4j.LoggerFactory.getLogger; + +/* + * Builder for GroupBucketEntry. + */ +public class GroupBucketEntryBuilder { + + private List ofBuckets; + private OFGroupType type; + + private final Logger log = getLogger(getClass()); + + /** + * Creates a builder. + * + * @param ofBuckets list of OFBucket + * @param type Group type + */ + public GroupBucketEntryBuilder(List ofBuckets, OFGroupType type) { + this.ofBuckets = ofBuckets; + this.type = type; + } + + /** + * Builds a GroupBuckets. + * + * @return GroupBuckets object, a list of GroupBuckets + */ + public GroupBuckets build() { + List bucketList = Lists.newArrayList(); + + for (OFBucket bucket: ofBuckets) { + TrafficTreatment treatment = buildTreatment(bucket.getActions()); + // TODO: Use GroupBucketEntry + GroupBucket groupBucket = null; + switch (type) { + case INDIRECT: + groupBucket = + DefaultGroupBucket.createIndirectGroupBucket(treatment); + break; + case SELECT: + groupBucket = + DefaultGroupBucket.createSelectGroupBucket(treatment); + break; + case FF: + PortNumber port = + PortNumber.portNumber(bucket.getWatchPort().getPortNumber()); + GroupId groupId = + new DefaultGroupId(bucket.getWatchGroup().getGroupNumber()); + groupBucket = + DefaultGroupBucket.createFailoverGroupBucket(treatment, + port, groupId); + break; + default: + log.error("Unsupported Group type : {}", type); + } + if (groupBucket != null) { + bucketList.add(groupBucket); + } + } + return new GroupBuckets(bucketList); + } + + + private TrafficTreatment buildTreatment(List actions) { + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); + // If this is a drop rule + if (actions.size() == 0) { + builder.drop(); + return builder.build(); + } + for (OFAction act : actions) { + switch (act.getType()) { + case OUTPUT: + OFActionOutput out = (OFActionOutput) act; + builder.setOutput( + PortNumber.portNumber(out.getPort().getPortNumber())); + break; + case SET_VLAN_VID: + OFActionSetVlanVid vlan = (OFActionSetVlanVid) act; + builder.setVlanId(VlanId.vlanId(vlan.getVlanVid().getVlan())); + break; + case SET_VLAN_PCP: + OFActionSetVlanPcp pcp = (OFActionSetVlanPcp) act; + builder.setVlanPcp(pcp.getVlanPcp().getValue()); + break; + case POP_VLAN: + builder.popVlan(); + break; + case PUSH_VLAN: + builder.pushVlan(); + break; + case SET_DL_DST: + OFActionSetDlDst dldst = (OFActionSetDlDst) act; + builder.setEthDst( + MacAddress.valueOf(dldst.getDlAddr().getLong())); + break; + case SET_DL_SRC: + OFActionSetDlSrc dlsrc = (OFActionSetDlSrc) act; + builder.setEthSrc( + MacAddress.valueOf(dlsrc.getDlAddr().getLong())); + + break; + case SET_NW_DST: + OFActionSetNwDst nwdst = (OFActionSetNwDst) act; + IPv4Address di = nwdst.getNwAddr(); + builder.setIpDst(Ip4Address.valueOf(di.getInt())); + break; + case SET_NW_SRC: + OFActionSetNwSrc nwsrc = (OFActionSetNwSrc) act; + IPv4Address si = nwsrc.getNwAddr(); + builder.setIpSrc(Ip4Address.valueOf(si.getInt())); + break; + case EXPERIMENTER: + OFActionExperimenter exp = (OFActionExperimenter) act; + if (exp.getExperimenter() == 0x80005A06 || + exp.getExperimenter() == 0x748771) { + OFActionCircuit ct = (OFActionCircuit) exp; + short lambda = ((OFOxmOchSigidBasic) ct.getField()).getValue().getChannelNumber(); + builder.add(Instructions.modL0Lambda(Lambda.indexedLambda(lambda))); + } else { + log.warn("Unsupported OFActionExperimenter {}", exp.getExperimenter()); + } + break; + case SET_FIELD: + OFActionSetField setField = (OFActionSetField) act; + handleSetField(builder, setField.getField()); + break; + case POP_MPLS: + OFActionPopMpls popMpls = (OFActionPopMpls) act; + builder.popMpls((short) popMpls.getEthertype().getValue()); + break; + case PUSH_MPLS: + OFActionPushMpls pushMpls = (OFActionPushMpls) act; + builder.pushMpls(); + break; + case COPY_TTL_IN: + OFActionCopyTtlIn copyTtlIn = (OFActionCopyTtlIn) act; + builder.copyTtlIn(); + break; + case COPY_TTL_OUT: + OFActionCopyTtlOut copyTtlOut = (OFActionCopyTtlOut) act; + builder.copyTtlOut(); + break; + case DEC_MPLS_TTL: + OFActionDecMplsTtl decMplsTtl = (OFActionDecMplsTtl) act; + builder.decMplsTtl(); + break; + case DEC_NW_TTL: + OFActionDecNwTtl decNwTtl = (OFActionDecNwTtl) act; + builder.decNwTtl(); + break; + case GROUP: + OFActionGroup grp = (OFActionGroup) act; + builder.group(new DefaultGroupId(grp.getGroup().getGroupNumber())); + break; + case SET_TP_DST: + case SET_TP_SRC: + case POP_PBB: + case PUSH_PBB: + case SET_MPLS_LABEL: + case SET_MPLS_TC: + case SET_MPLS_TTL: + case SET_NW_ECN: + case SET_NW_TOS: + case SET_NW_TTL: + case SET_QUEUE: + case STRIP_VLAN: + case ENQUEUE: + default: + log.warn("Action type {} not yet implemented.", act.getType()); + } + } + + return builder.build(); + } + + private void handleSetField(TrafficTreatment.Builder builder, OFOxm oxm) { + switch (oxm.getMatchField().id) { + case VLAN_PCP: + @SuppressWarnings("unchecked") + OFOxm vlanpcp = (OFOxm) oxm; + builder.setVlanPcp(vlanpcp.getValue().getValue()); + break; + case VLAN_VID: + @SuppressWarnings("unchecked") + OFOxm vlanvid = (OFOxm) oxm; + builder.setVlanId(VlanId.vlanId(vlanvid.getValue().getVlan())); + break; + case ETH_DST: + @SuppressWarnings("unchecked") + OFOxm ethdst = + (OFOxm) oxm; + builder.setEthDst(MacAddress.valueOf(ethdst.getValue().getLong())); + break; + case ETH_SRC: + @SuppressWarnings("unchecked") + OFOxm ethsrc = + (OFOxm) oxm; + builder.setEthSrc(MacAddress.valueOf(ethsrc.getValue().getLong())); + break; + case IPV4_DST: + @SuppressWarnings("unchecked") + OFOxm ip4dst = (OFOxm) oxm; + builder.setIpDst(Ip4Address.valueOf(ip4dst.getValue().getInt())); + break; + case IPV4_SRC: + @SuppressWarnings("unchecked") + OFOxm ip4src = (OFOxm) oxm; + builder.setIpSrc(Ip4Address.valueOf(ip4src.getValue().getInt())); + break; + case MPLS_LABEL: + @SuppressWarnings("unchecked") + OFOxm labelId = (OFOxm) oxm; + builder.setMpls(MplsLabel.mplsLabel((int) labelId.getValue().getValue())); + break; + case MPLS_BOS: + @SuppressWarnings("unchecked") + OFOxm mplsBos = (OFOxm) oxm; + builder.setMplsBos(mplsBos.getValue() == U8.ZERO ? false : true); + break; + case ARP_OP: + case ARP_SHA: + case ARP_SPA: + case ARP_THA: + case ARP_TPA: + case BSN_EGR_PORT_GROUP_ID: + case BSN_GLOBAL_VRF_ALLOWED: + case BSN_IN_PORTS_128: + case BSN_L3_DST_CLASS_ID: + case BSN_L3_INTERFACE_CLASS_ID: + case BSN_L3_SRC_CLASS_ID: + case BSN_LAG_ID: + case BSN_TCP_FLAGS: + case BSN_UDF0: + case BSN_UDF1: + case BSN_UDF2: + case BSN_UDF3: + case BSN_UDF4: + case BSN_UDF5: + case BSN_UDF6: + case BSN_UDF7: + case BSN_VLAN_XLATE_PORT_GROUP_ID: + case BSN_VRF: + case ETH_TYPE: + case ICMPV4_CODE: + case ICMPV4_TYPE: + case ICMPV6_CODE: + case ICMPV6_TYPE: + case IN_PHY_PORT: + case IN_PORT: + case IPV6_DST: + case IPV6_FLABEL: + case IPV6_ND_SLL: + case IPV6_ND_TARGET: + case IPV6_ND_TLL: + case IPV6_SRC: + case IP_DSCP: + case IP_ECN: + case IP_PROTO: + case METADATA: + case MPLS_TC: + case OCH_SIGID: + case OCH_SIGID_BASIC: + case OCH_SIGTYPE: + case OCH_SIGTYPE_BASIC: + case SCTP_DST: + case SCTP_SRC: + case TCP_DST: + case TCP_SRC: + case TUNNEL_ID: + case UDP_DST: + case UDP_SRC: + default: + log.warn("Set field type {} not yet implemented.", oxm.getMatchField().id); + break; + } + } +} diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java new file mode 100644 index 00000000..d5804f44 --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java @@ -0,0 +1,376 @@ +/* + * 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.provider.of.group.impl; + +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip6Address; +import org.onosproject.core.GroupId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.flow.instructions.L0ModificationInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction; +import org.onosproject.net.flow.instructions.L3ModificationInstruction; +import org.onosproject.net.group.GroupBucket; +import org.onosproject.net.group.GroupBuckets; +import org.onosproject.net.group.GroupDescription; +import org.projectfloodlight.openflow.protocol.OFBucket; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFGroupAdd; +import org.projectfloodlight.openflow.protocol.OFGroupDelete; +import org.projectfloodlight.openflow.protocol.OFGroupMod; +import org.projectfloodlight.openflow.protocol.OFGroupType; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.action.OFActionGroup; +import org.projectfloodlight.openflow.protocol.action.OFActionOutput; +import org.projectfloodlight.openflow.protocol.oxm.OFOxm; +import org.projectfloodlight.openflow.types.CircuitSignalID; +import org.projectfloodlight.openflow.types.EthType; +import org.projectfloodlight.openflow.types.IPv4Address; +import org.projectfloodlight.openflow.types.IPv6Address; +import org.projectfloodlight.openflow.types.IPv6FlowLabel; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.OFBooleanValue; +import org.projectfloodlight.openflow.types.OFGroup; +import org.projectfloodlight.openflow.types.OFPort; +import org.projectfloodlight.openflow.types.OFVlanVidMatch; +import org.projectfloodlight.openflow.types.U32; +import org.projectfloodlight.openflow.types.VlanPcp; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +import static org.slf4j.LoggerFactory.getLogger; + +/* + * Builder for GroupMod. + */ +public final class GroupModBuilder { + + private GroupBuckets buckets; + private GroupId groupId; + private GroupDescription.Type type; + private OFFactory factory; + private Long xid; + + private final Logger log = getLogger(getClass()); + + private static final int OFPCML_NO_BUFFER = 0xffff; + + private GroupModBuilder(GroupBuckets buckets, GroupId groupId, + GroupDescription.Type type, OFFactory factory, + Optional xid) { + this.buckets = buckets; + this.groupId = groupId; + this.type = type; + this.factory = factory; + this.xid = xid.orElse((long) 0); + } + + /** + * Creates a builder for GroupMod. + * + * @param buckets GroupBuckets object + * @param groupId Group Id to create + * @param type Group type + * @param factory OFFactory object + * @param xid transaction ID + * @return GroupModBuilder object + */ + public static GroupModBuilder builder(GroupBuckets buckets, GroupId groupId, + GroupDescription.Type type, OFFactory factory, + Optional xid) { + + return new GroupModBuilder(buckets, groupId, type, factory, xid); + } + + /** + * Builds the GroupAdd OF message. + * + * @return GroupAdd OF message + */ + public OFGroupAdd buildGroupAdd() { + + List ofBuckets = new ArrayList(); + for (GroupBucket bucket: buckets.buckets()) { + List actions = buildActions(bucket.treatment()); + + OFBucket.Builder bucketBuilder = factory.buildBucket(); + bucketBuilder.setActions(actions); + if (type == GroupDescription.Type.SELECT) { + bucketBuilder.setWeight(1); + } + bucketBuilder.setWatchGroup(OFGroup.ANY); + bucketBuilder.setWatchPort(OFPort.ANY); + OFBucket ofBucket = bucketBuilder.build(); + ofBuckets.add(ofBucket); + } + + OFGroupAdd groupMsg = factory.buildGroupAdd() + .setGroup(OFGroup.of(groupId.id())) + .setBuckets(ofBuckets) + .setGroupType(getOFGroupType(type)) + .setXid(xid) + .build(); + + return groupMsg; + } + + /** + * Builds the GroupMod OF message. + * + * @return GroupMod OF message + */ + public OFGroupMod buildGroupMod() { + List ofBuckets = new ArrayList(); + for (GroupBucket bucket: buckets.buckets()) { + List actions = buildActions(bucket.treatment()); + + OFBucket.Builder bucketBuilder = factory.buildBucket(); + bucketBuilder.setActions(actions); + if (type == GroupDescription.Type.SELECT) { + bucketBuilder.setWeight(1); + } + bucketBuilder.setWatchGroup(OFGroup.ANY); + bucketBuilder.setWatchPort(OFPort.ANY); + OFBucket ofBucket = bucketBuilder.build(); + ofBuckets.add(ofBucket); + } + + OFGroupMod groupMsg = factory.buildGroupModify() + .setGroup(OFGroup.of(groupId.id())) + .setBuckets(ofBuckets) + .setGroupType(getOFGroupType(type)) + .setXid(xid) + .build(); + + return groupMsg; + } + + /** + * Builds the GroupDel OF message. + * + * @return GroupDel OF message + */ + public OFGroupDelete buildGroupDel() { + + OFGroupDelete groupMsg = factory.buildGroupDelete() + .setGroup(OFGroup.of(groupId.id())) + .setGroupType(OFGroupType.SELECT) + .setXid(xid) + .build(); + + return groupMsg; + } + + private List buildActions(TrafficTreatment treatment) { + if (treatment == null) { + return Collections.emptyList(); + } + + List actions = new LinkedList<>(); + for (Instruction i : treatment.allInstructions()) { + switch (i.type()) { + case DROP: + log.warn("Saw drop action; assigning drop action"); + return Collections.emptyList(); + case L0MODIFICATION: + actions.add(buildL0Modification(i)); + break; + case L2MODIFICATION: + actions.add(buildL2Modification(i)); + break; + case L3MODIFICATION: + actions.add(buildL3Modification(i)); + break; + case OUTPUT: + Instructions.OutputInstruction out = + (Instructions.OutputInstruction) i; + OFActionOutput.Builder action = factory.actions().buildOutput() + .setPort(OFPort.of((int) out.port().toLong())); + if (out.port().equals(PortNumber.CONTROLLER)) { + action.setMaxLen(OFPCML_NO_BUFFER); + } + actions.add(action.build()); + break; + case GROUP: + Instructions.GroupInstruction grp = + (Instructions.GroupInstruction) i; + OFActionGroup.Builder actgrp = factory.actions().buildGroup() + .setGroup(OFGroup.of(grp.groupId().id())); + actions.add(actgrp.build()); + break; + default: + log.warn("Instruction type {} not yet implemented.", i.type()); + } + } + + return actions; + } + + private OFAction buildL0Modification(Instruction i) { + L0ModificationInstruction l0m = (L0ModificationInstruction) i; + switch (l0m.subtype()) { + case LAMBDA: + L0ModificationInstruction.ModLambdaInstruction ml = + (L0ModificationInstruction.ModLambdaInstruction) i; + return factory.actions().circuit(factory.oxms().ochSigidBasic( + new CircuitSignalID((byte) 1, (byte) 2, ml.lambda(), (short) 1))); + default: + log.warn("Unimplemented action type {}.", l0m.subtype()); + break; + } + return null; + } + + private OFAction buildL2Modification(Instruction i) { + L2ModificationInstruction l2m = (L2ModificationInstruction) i; + L2ModificationInstruction.ModEtherInstruction eth; + OFOxm oxm = null; + switch (l2m.subtype()) { + case ETH_DST: + eth = (L2ModificationInstruction.ModEtherInstruction) l2m; + oxm = factory.oxms().ethDst(MacAddress.of(eth.mac().toLong())); + break; + case ETH_SRC: + eth = (L2ModificationInstruction.ModEtherInstruction) l2m; + oxm = factory.oxms().ethSrc(MacAddress.of(eth.mac().toLong())); + break; + case VLAN_ID: + L2ModificationInstruction.ModVlanIdInstruction vlanId = + (L2ModificationInstruction.ModVlanIdInstruction) l2m; + oxm = factory.oxms().vlanVid(OFVlanVidMatch.ofVlan(vlanId.vlanId().toShort())); + break; + case VLAN_PCP: + L2ModificationInstruction.ModVlanPcpInstruction vlanPcp = + (L2ModificationInstruction.ModVlanPcpInstruction) l2m; + oxm = factory.oxms().vlanPcp(VlanPcp.of(vlanPcp.vlanPcp())); + break; + case VLAN_POP: + return factory.actions().popVlan(); + case VLAN_PUSH: + L2ModificationInstruction.PushHeaderInstructions pushVlanInstruction + = (L2ModificationInstruction.PushHeaderInstructions) l2m; + return factory.actions().pushVlan( + EthType.of(pushVlanInstruction.ethernetType().toShort())); + case MPLS_PUSH: + L2ModificationInstruction.PushHeaderInstructions pushHeaderInstructions = + (L2ModificationInstruction.PushHeaderInstructions) l2m; + return factory.actions().pushMpls(EthType.of(pushHeaderInstructions + .ethernetType().toShort())); + case MPLS_POP: + L2ModificationInstruction.PushHeaderInstructions popHeaderInstructions = + (L2ModificationInstruction.PushHeaderInstructions) l2m; + return factory.actions().popMpls(EthType.of(popHeaderInstructions + .ethernetType().toShort())); + case MPLS_LABEL: + L2ModificationInstruction.ModMplsLabelInstruction mplsLabel = + (L2ModificationInstruction.ModMplsLabelInstruction) l2m; + oxm = factory.oxms().mplsLabel(U32.of(mplsLabel.mplsLabel().toInt())); + break; + case MPLS_BOS: + L2ModificationInstruction.ModMplsBosInstruction mplsBos = + (L2ModificationInstruction.ModMplsBosInstruction) l2m; + oxm = factory.oxms() + .mplsBos(mplsBos.mplsBos() ? OFBooleanValue.TRUE + : OFBooleanValue.FALSE); + break; + case DEC_MPLS_TTL: + return factory.actions().decMplsTtl(); + default: + log.warn("Unimplemented action type {}.", l2m.subtype()); + break; + } + + if (oxm != null) { + return factory.actions().buildSetField().setField(oxm).build(); + } + return null; + } + + private OFAction buildL3Modification(Instruction i) { + L3ModificationInstruction l3m = (L3ModificationInstruction) i; + L3ModificationInstruction.ModIPInstruction ip; + Ip4Address ip4; + Ip6Address ip6; + OFOxm oxm = null; + switch (l3m.subtype()) { + case IPV4_SRC: + ip = (L3ModificationInstruction.ModIPInstruction) i; + ip4 = ip.ip().getIp4Address(); + oxm = factory.oxms().ipv4Src(IPv4Address.of(ip4.toInt())); + break; + case IPV4_DST: + ip = (L3ModificationInstruction.ModIPInstruction) i; + ip4 = ip.ip().getIp4Address(); + oxm = factory.oxms().ipv4Dst(IPv4Address.of(ip4.toInt())); + break; + case IPV6_SRC: + ip = (L3ModificationInstruction.ModIPInstruction) i; + ip6 = ip.ip().getIp6Address(); + oxm = factory.oxms().ipv6Src(IPv6Address.of(ip6.toOctets())); + break; + case IPV6_DST: + ip = (L3ModificationInstruction.ModIPInstruction) i; + ip6 = ip.ip().getIp6Address(); + oxm = factory.oxms().ipv6Dst(IPv6Address.of(ip6.toOctets())); + break; + case IPV6_FLABEL: + L3ModificationInstruction.ModIPv6FlowLabelInstruction flowLabelInstruction = + (L3ModificationInstruction.ModIPv6FlowLabelInstruction) i; + int flowLabel = flowLabelInstruction.flowLabel(); + oxm = factory.oxms().ipv6Flabel(IPv6FlowLabel.of(flowLabel)); + break; + case DEC_TTL: + return factory.actions().decNwTtl(); + case TTL_IN: + return factory.actions().copyTtlIn(); + case TTL_OUT: + return factory.actions().copyTtlOut(); + default: + log.warn("Unimplemented action type {}.", l3m.subtype()); + break; + } + + if (oxm != null) { + return factory.actions().buildSetField().setField(oxm).build(); + } + return null; + } + + private OFGroupType getOFGroupType(GroupDescription.Type groupType) { + switch (groupType) { + case INDIRECT: + return OFGroupType.INDIRECT; + case SELECT: + return OFGroupType.SELECT; + case FAILOVER: + return OFGroupType.FF; + case ALL: + return OFGroupType.ALL; + default: + log.error("Unsupported group type : {}", groupType); + break; + } + return null; + } +} + diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java new file mode 100644 index 00000000..9816426b --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java @@ -0,0 +1,111 @@ +/* + * 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.provider.of.group.impl; + +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.util.Timer; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFGroupDescStatsRequest; +import org.projectfloodlight.openflow.protocol.OFGroupStatsRequest; +import org.projectfloodlight.openflow.types.OFGroup; +import org.slf4j.Logger; + +import java.util.concurrent.TimeUnit; + +import static org.slf4j.LoggerFactory.getLogger; + +/* + * Sends Group Stats Request and collect the group statistics with a time interval. + */ +public class GroupStatsCollector implements TimerTask { + + private final HashedWheelTimer timer = Timer.getTimer(); + private final OpenFlowSwitch sw; + private final Logger log = getLogger(getClass()); + private final int refreshInterval; + + private Timeout timeout; + + private boolean stopTimer = false; + + /** + * Creates a GroupStatsCollector object. + * + * @param sw Open Flow switch + * @param interval time interval for collecting group statistic + */ + public GroupStatsCollector(OpenFlowSwitch sw, int interval) { + this.sw = sw; + this.refreshInterval = interval; + } + + @Override + public void run(Timeout timeout) throws Exception { + log.trace("Collecting stats for {}", sw.getStringId()); + + sendGroupStatistic(); + + if (!this.stopTimer) { + log.trace("Scheduling stats collection in {} seconds for {}", + this.refreshInterval, this.sw.getStringId()); + timeout.getTimer().newTimeout(this, refreshInterval, + TimeUnit.SECONDS); + } + } + + private void sendGroupStatistic() { + if (log.isTraceEnabled()) { + log.trace("sendGroupStatistics {}:{}", sw.getStringId(), sw.getRole()); + } + if (sw.getRole() != RoleState.MASTER) { + return; + } + Long statsXid = OpenFlowGroupProvider.getXidAndAdd(2); + OFGroupStatsRequest statsRequest = sw.factory().buildGroupStatsRequest() + .setGroup(OFGroup.ALL) + .setXid(statsXid) + .build(); + sw.sendMsg(statsRequest); + + Long descXid = statsXid + 1; + OFGroupDescStatsRequest descStatsRequest = + sw.factory().buildGroupDescStatsRequest() + .setXid(descXid) + .build(); + sw.sendMsg(descStatsRequest); + } + + /** + * Starts the collector. + */ + public void start() { + log.info("Starting Group Stats collection thread for {}", sw.getStringId()); + timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS); + } + + /** + * Stops the collector. + */ + public void stop() { + log.info("Stopping Group Stats collection thread for {}", sw.getStringId()); + this.stopTimer = true; + timeout.cancel(); + } +} diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java new file mode 100644 index 00000000..78650fe6 --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java @@ -0,0 +1,366 @@ +/* + * 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.provider.of.group.impl; + +import com.google.common.collect.Maps; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onosproject.core.DefaultGroupId; +import org.onosproject.core.GroupId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.group.DefaultGroup; +import org.onosproject.net.group.Group; +import org.onosproject.net.group.GroupBuckets; +import org.onosproject.net.group.GroupDescription; +import org.onosproject.net.group.GroupOperation; +import org.onosproject.net.group.GroupOperations; +import org.onosproject.net.group.GroupProvider; +import org.onosproject.net.group.GroupProviderRegistry; +import org.onosproject.net.group.GroupProviderService; +import org.onosproject.net.group.StoredGroupBucketEntry; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowEventListener; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitchListener; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFBucketCounter; +import org.projectfloodlight.openflow.protocol.OFErrorMsg; +import org.projectfloodlight.openflow.protocol.OFErrorType; +import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry; +import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; +import org.projectfloodlight.openflow.protocol.OFGroupMod; +import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry; +import org.projectfloodlight.openflow.protocol.OFGroupStatsReply; +import org.projectfloodlight.openflow.protocol.OFGroupType; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortStatus; +import org.projectfloodlight.openflow.protocol.OFStatsReply; +import org.projectfloodlight.openflow.protocol.OFStatsType; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.slf4j.Logger; + +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Provider which uses an OpenFlow controller to handle Group. + */ +@Component(immediate = true) +public class OpenFlowGroupProvider extends AbstractProvider implements GroupProvider { + + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenFlowController controller; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected GroupProviderRegistry providerRegistry; + + private GroupProviderService providerService; + + static final int POLL_INTERVAL = 10; + + private final InternalGroupProvider listener = new InternalGroupProvider(); + + private static final AtomicLong XID_COUNTER = new AtomicLong(1); + private final Map collectors = Maps.newHashMap(); + private final Map groupStats = Maps.newConcurrentMap(); + private final Map pendingGroupOperations = + Maps.newConcurrentMap(); + + /* Map */ + private final Map pendingXidMaps = Maps.newConcurrentMap(); + + /** + * Creates a OpenFlow group provider. + */ + public OpenFlowGroupProvider() { + super(new ProviderId("of", "org.onosproject.provider.group")); + } + + @Activate + public void activate() { + providerService = providerRegistry.register(this); + controller.addListener(listener); + controller.addEventListener(listener); + + for (OpenFlowSwitch sw : controller.getSwitches()) { + if (isGroupSupported(sw)) { + GroupStatsCollector gsc = new GroupStatsCollector(sw, POLL_INTERVAL); + gsc.start(); + collectors.put(new Dpid(sw.getId()), gsc); + } + } + + log.info("Started"); + } + + @Deactivate + public void deactivate() { + providerRegistry.unregister(this); + providerService = null; + + log.info("Stopped"); + } + + @Override + public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) { + Map mods = Maps.newIdentityHashMap(); + final Dpid dpid = Dpid.dpid(deviceId.uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + for (GroupOperation groupOperation: groupOps.operations()) { + if (sw == null) { + log.error("SW {} is not found", dpid); + return; + } + final Long groupModXid = XID_COUNTER.getAndIncrement(); + GroupModBuilder builder = + GroupModBuilder.builder(groupOperation.buckets(), + groupOperation.groupId(), + groupOperation.groupType(), + sw.factory(), + Optional.of(groupModXid)); + OFGroupMod groupMod = null; + switch (groupOperation.opType()) { + case ADD: + groupMod = builder.buildGroupAdd(); + break; + case MODIFY: + groupMod = builder.buildGroupMod(); + break; + case DELETE: + groupMod = builder.buildGroupDel(); + break; + default: + log.error("Unsupported Group operation"); + } + sw.sendMsg(groupMod); + GroupId groudId = new DefaultGroupId(groupMod.getGroup().getGroupNumber()); + pendingGroupOperations.put(groudId, groupOperation); + pendingXidMaps.put(groudId, groupModXid); + } + } + + private void pushGroupMetrics(Dpid dpid, OFStatsReply statsReply) { + DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid)); + + OFGroupStatsReply groupStatsReply = null; + OFGroupDescStatsReply groupDescStatsReply = null; + + synchronized (groupStats) { + if (statsReply.getStatsType() == OFStatsType.GROUP) { + OFStatsReply reply = groupStats.get(statsReply.getXid() + 1); + if (reply != null) { + groupStatsReply = (OFGroupStatsReply) statsReply; + groupDescStatsReply = (OFGroupDescStatsReply) reply; + groupStats.remove(statsReply.getXid() + 1); + } else { + groupStats.put(statsReply.getXid(), statsReply); + } + } else if (statsReply.getStatsType() == OFStatsType.GROUP_DESC) { + OFStatsReply reply = groupStats.get(statsReply.getXid() - 1); + if (reply != null) { + groupStatsReply = (OFGroupStatsReply) reply; + groupDescStatsReply = (OFGroupDescStatsReply) statsReply; + groupStats.remove(statsReply.getXid() - 1); + } else { + groupStats.put(statsReply.getXid(), statsReply); + } + } + } + + if (groupStatsReply != null && groupDescStatsReply != null) { + Collection groups = buildGroupMetrics(deviceId, + groupStatsReply, groupDescStatsReply); + providerService.pushGroupMetrics(deviceId, groups); + for (Group group: groups) { + pendingGroupOperations.remove(group.id()); + pendingXidMaps.remove(group.id()); + } + } + } + + private Collection buildGroupMetrics(DeviceId deviceId, + OFGroupStatsReply groupStatsReply, + OFGroupDescStatsReply groupDescStatsReply) { + + Map groups = Maps.newHashMap(); + + + for (OFGroupDescStatsEntry entry: groupDescStatsReply.getEntries()) { + int id = entry.getGroup().getGroupNumber(); + GroupId groupId = new DefaultGroupId(id); + GroupDescription.Type type = getGroupType(entry.getGroupType()); + GroupBuckets buckets = new GroupBucketEntryBuilder(entry.getBuckets(), + entry.getGroupType()).build(); + DefaultGroup group = new DefaultGroup(groupId, deviceId, type, buckets); + groups.put(id, group); + } + + for (OFGroupStatsEntry entry: groupStatsReply.getEntries()) { + int groupId = entry.getGroup().getGroupNumber(); + DefaultGroup group = (DefaultGroup) groups.get(groupId); + if (group != null) { + group.setBytes(entry.getByteCount().getValue()); + group.setLife(entry.getDurationSec()); + group.setPackets(entry.getPacketCount().getValue()); + group.setReferenceCount(entry.getRefCount()); + int bucketIndex = 0; + for (OFBucketCounter bucketStats:entry.getBucketStats()) { + ((StoredGroupBucketEntry) group.buckets().buckets() + .get(bucketIndex)) + .setPackets(bucketStats + .getPacketCount().getValue()); + ((StoredGroupBucketEntry) group.buckets().buckets() + .get(bucketIndex)) + .setBytes(entry.getBucketStats() + .get(bucketIndex) + .getByteCount().getValue()); + bucketIndex++; + } + } + } + + return groups.values(); + } + + private GroupDescription.Type getGroupType(OFGroupType type) { + switch (type) { + case ALL: + return GroupDescription.Type.ALL; + case INDIRECT: + return GroupDescription.Type.INDIRECT; + case SELECT: + return GroupDescription.Type.SELECT; + case FF: + return GroupDescription.Type.FAILOVER; + default: + log.error("Unsupported OF group type : {}", type); + break; + } + return null; + } + + /** + * Returns a transaction ID for entire group operations and increases + * the counter by the number given. + * + * @param increase the amount to increase the counter by + * @return a transaction ID + */ + public static long getXidAndAdd(int increase) { + return XID_COUNTER.getAndAdd(increase); + } + + private boolean isGroupSupported(OpenFlowSwitch sw) { + if (sw.factory().getVersion() == OFVersion.OF_10 || + sw.factory().getVersion() == OFVersion.OF_11 || + sw.factory().getVersion() == OFVersion.OF_12) { + return false; + } + + return true; + } + + private class InternalGroupProvider + implements OpenFlowSwitchListener, OpenFlowEventListener { + + @Override + public void handleMessage(Dpid dpid, OFMessage msg) { + switch (msg.getType()) { + case STATS_REPLY: + pushGroupMetrics(dpid, (OFStatsReply) msg); + break; + case ERROR: + OFErrorMsg errorMsg = (OFErrorMsg) msg; + if (errorMsg.getErrType() == OFErrorType.GROUP_MOD_FAILED) { + GroupId pendingGroupId = null; + for (Map.Entry entry: pendingXidMaps.entrySet()) { + if (entry.getValue() == errorMsg.getXid()) { + pendingGroupId = entry.getKey(); + break; + } + } + if (pendingGroupId == null) { + log.warn("Error for unknown group operation: {}", + errorMsg.getXid()); + } else { + GroupOperation operation = + pendingGroupOperations.get(pendingGroupId); + DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid)); + if (operation != null) { + providerService.groupOperationFailed(deviceId, + operation); + pendingGroupOperations.remove(pendingGroupId); + pendingXidMaps.remove(pendingGroupId); + log.warn("Received an group mod error {}", msg); + } else { + log.error("Cannot find pending group operation with group ID: {}", + pendingGroupId); + } + } + break; + } + default: + break; + } + } + + @Override + public void switchAdded(Dpid dpid) { + OpenFlowSwitch sw = controller.getSwitch(dpid); + if (isGroupSupported(sw)) { + GroupStatsCollector gsc = new GroupStatsCollector( + controller.getSwitch(dpid), POLL_INTERVAL); + gsc.start(); + collectors.put(dpid, gsc); + } + } + + @Override + public void switchRemoved(Dpid dpid) { + GroupStatsCollector collector = collectors.remove(dpid); + if (collector != null) { + collector.stop(); + } + } + + @Override + public void switchChanged(Dpid dpid) { + } + + @Override + public void portChanged(Dpid dpid, OFPortStatus status) { + } + + @Override + public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) { + } + } + +} diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java new file mode 100644 index 00000000..9fda4a31 --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Provider that uses OpenFlow controller as a means of device port group management. + */ +package org.onosproject.provider.of.group.impl; \ No newline at end of file diff --git a/framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java b/framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java new file mode 100644 index 00000000..d66ba090 --- /dev/null +++ b/framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java @@ -0,0 +1,397 @@ +package org.onosproject.provider.of.group.impl; + +import com.google.common.collect.Lists; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.core.DefaultGroupId; +import org.onosproject.core.GroupId; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.group.DefaultGroupBucket; +import org.onosproject.net.group.Group; +import org.onosproject.net.group.GroupBucket; +import org.onosproject.net.group.GroupBuckets; +import org.onosproject.net.group.GroupDescription; +import org.onosproject.net.group.GroupOperation; +import org.onosproject.net.group.GroupOperations; +import org.onosproject.net.group.GroupProvider; +import org.onosproject.net.group.GroupProviderRegistry; +import org.onosproject.net.group.GroupProviderService; +import org.onosproject.net.provider.AbstractProviderService; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowEventListener; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitchListener; +import org.onosproject.openflow.controller.PacketListener; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFFactories; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; +import org.projectfloodlight.openflow.protocol.OFGroupMod; +import org.projectfloodlight.openflow.protocol.OFGroupModFailedCode; +import org.projectfloodlight.openflow.protocol.OFGroupStatsReply; +import org.projectfloodlight.openflow.protocol.OFGroupType; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.protocol.errormsg.OFGroupModFailedErrorMsg; +import org.projectfloodlight.openflow.types.OFGroup; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.*; + +public class OpenFlowGroupProviderTest { + + OpenFlowGroupProvider provider = new OpenFlowGroupProvider(); + private final OpenFlowController controller = new TestController(); + GroupProviderRegistry providerRegistry = new TestGroupProviderRegistry(); + GroupProviderService providerService; + + private DeviceId deviceId = DeviceId.deviceId("of:0000000000000001"); + private Dpid dpid1 = Dpid.dpid(deviceId.uri()); + + @Before + public void setUp() { + provider.controller = controller; + provider.providerRegistry = providerRegistry; + provider.activate(); + } + + @Test + public void basics() { + assertNotNull("registration expected", providerService); + assertEquals("incorrect provider", provider, providerService.provider()); + } + + @Test + public void addGroup() { + + GroupId groupId = new DefaultGroupId(1); + + List bucketList = Lists.newArrayList(); + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); + builder.setOutput(PortNumber.portNumber(1)); + GroupBucket bucket = + DefaultGroupBucket.createSelectGroupBucket(builder.build()); + bucketList.add(bucket); + GroupBuckets buckets = new GroupBuckets(bucketList); + + List operationList = Lists.newArrayList(); + GroupOperation operation = GroupOperation.createAddGroupOperation(groupId, + GroupDescription.Type.SELECT, buckets); + operationList.add(operation); + GroupOperations operations = new GroupOperations(operationList); + + provider.performGroupOperation(deviceId, operations); + + final Dpid dpid = Dpid.dpid(deviceId.uri()); + TestOpenFlowSwitch sw = (TestOpenFlowSwitch) controller.getSwitch(dpid); + assertNotNull("Switch should not be nul", sw); + assertNotNull("OFGroupMsg should not be null", sw.msg); + + } + + + @Test + public void groupModFailure() { + TestOpenFlowGroupProviderService testProviderService = + (TestOpenFlowGroupProviderService) providerService; + + GroupId groupId = new DefaultGroupId(1); + List bucketList = Lists.newArrayList(); + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); + builder.setOutput(PortNumber.portNumber(1)); + GroupBucket bucket = + DefaultGroupBucket.createSelectGroupBucket(builder.build()); + bucketList.add(bucket); + GroupBuckets buckets = new GroupBuckets(bucketList); + List operationList = Lists.newArrayList(); + GroupOperation operation = GroupOperation.createAddGroupOperation(groupId, + GroupDescription.Type.SELECT, buckets); + operationList.add(operation); + GroupOperations operations = new GroupOperations(operationList); + + provider.performGroupOperation(deviceId, operations); + + OFGroupModFailedErrorMsg.Builder errorBuilder = + OFFactories.getFactory(OFVersion.OF_13).errorMsgs().buildGroupModFailedErrorMsg(); + OFGroupMod.Builder groupBuilder = OFFactories.getFactory(OFVersion.OF_13).buildGroupModify(); + groupBuilder.setGroupType(OFGroupType.ALL); + groupBuilder.setGroup(OFGroup.of(1)); + errorBuilder.setCode(OFGroupModFailedCode.GROUP_EXISTS); + errorBuilder.setXid(provider.getXidAndAdd(0) - 1); + + controller.processPacket(dpid1, errorBuilder.build()); + + assertNotNull("Operation failed should not be null", + testProviderService.failedOperation); + } + + + @Test + public void groupStatsEvent() { + TestOpenFlowGroupProviderService testProviderService = + (TestOpenFlowGroupProviderService) providerService; + + OFGroupStatsReply.Builder rep1 = + OFFactories.getFactory(OFVersion.OF_13).buildGroupStatsReply(); + rep1.setXid(1); + controller.processPacket(dpid1, rep1.build()); + OFGroupDescStatsReply.Builder rep2 = + OFFactories.getFactory(OFVersion.OF_13).buildGroupDescStatsReply(); + assertNull("group entries is not set yet", testProviderService.getGroupEntries()); + + rep2.setXid(2); + controller.processPacket(dpid1, rep2.build()); + assertNotNull("group entries should be set", testProviderService.getGroupEntries()); + } + + + + @After + public void tearDown() { + provider.deactivate(); + provider.providerRegistry = null; + provider.controller = null; + } + + private class TestOpenFlowGroupProviderService + extends AbstractProviderService + implements GroupProviderService { + + Collection groups = null; + GroupOperation failedOperation = null; + + protected TestOpenFlowGroupProviderService(GroupProvider provider) { + super(provider); + } + + @Override + public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) { + this.failedOperation = operation; + } + + @Override + public void pushGroupMetrics(DeviceId deviceId, Collection groupEntries) { + this.groups = groupEntries; + } + + public Collection getGroupEntries() { + return groups; + } + } + + private class TestController implements OpenFlowController { + + OpenFlowEventListener eventListener = null; + List switches = Lists.newArrayList(); + + public TestController() { + OpenFlowSwitch testSwitch = new TestOpenFlowSwitch(); + switches.add(testSwitch); + } + + @Override + public void addListener(OpenFlowSwitchListener listener) { + } + + @Override + public void removeListener(OpenFlowSwitchListener listener) { + + } + + @Override + public void addPacketListener(int priority, PacketListener listener) { + + } + + @Override + public void removePacketListener(PacketListener listener) { + + } + + @Override + public void addEventListener(OpenFlowEventListener listener) { + this.eventListener = listener; + } + + @Override + public void removeEventListener(OpenFlowEventListener listener) { + + } + + @Override + public void write(Dpid dpid, OFMessage msg) { + + } + + @Override + public void processPacket(Dpid dpid, OFMessage msg) { + eventListener.handleMessage(dpid, msg); + } + + @Override + public void setRole(Dpid dpid, RoleState role) { + + } + + @Override + public Iterable getSwitches() { + return switches; + } + + @Override + public Iterable getMasterSwitches() { + return null; + } + + @Override + public Iterable getEqualSwitches() { + return null; + } + + @Override + public OpenFlowSwitch getSwitch(Dpid dpid) { + return switches.get(0); + } + + @Override + public OpenFlowSwitch getMasterSwitch(Dpid dpid) { + return null; + } + + @Override + public OpenFlowSwitch getEqualSwitch(Dpid dpid) { + return null; + } + + } + + private class TestGroupProviderRegistry implements GroupProviderRegistry { + + @Override + public GroupProviderService register(GroupProvider provider) { + providerService = new TestOpenFlowGroupProviderService(provider); + return providerService; + } + + @Override + public void unregister(GroupProvider provider) { + } + + @Override + public Set getProviders() { + return null; + } + } + + private class TestOpenFlowSwitch implements OpenFlowSwitch { + + OFMessage msg = null; + + @Override + public void sendMsg(OFMessage msg) { + this.msg = msg; + } + + @Override + public void sendMsg(List msgs) { + + } + + @Override + public void handleMessage(OFMessage fromSwitch) { + + } + + @Override + public void setRole(RoleState role) { + + } + + @Override + public RoleState getRole() { + return null; + } + + @Override + public List getPorts() { + return null; + } + + @Override + public OFFactory factory() { + return OFFactories.getFactory(OFVersion.OF_13); + } + + @Override + public String getStringId() { + return null; + } + + @Override + public long getId() { + return 0; + } + + @Override + public String manufacturerDescription() { + return null; + } + + @Override + public String datapathDescription() { + return null; + } + + @Override + public String hardwareDescription() { + return null; + } + + @Override + public String softwareDescription() { + return null; + } + + @Override + public String serialNumber() { + return null; + } + + @Override + public boolean isConnected() { + return false; + } + + @Override + public void disconnectSwitch() { + + } + + @Override + public void returnRoleReply(RoleState requested, RoleState response) { + + } + + @Override + public Device.Type deviceType() { + return Device.Type.SWITCH; + } + + @Override + public String channelId() { + return null; + } + + } +} \ No newline at end of file diff --git a/framework/src/onos/providers/openflow/meter/pom.xml b/framework/src/onos/providers/openflow/meter/pom.xml new file mode 100644 index 00000000..9de5c1b0 --- /dev/null +++ b/framework/src/onos/providers/openflow/meter/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + + org.onosproject + onos-of-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-of-provider-meter + bundle + + ONOS OpenFlow protocol meter provider + + \ No newline at end of file diff --git a/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java new file mode 100644 index 00000000..c07354b6 --- /dev/null +++ b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java @@ -0,0 +1,159 @@ +/* + * 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.provider.of.meter.impl; + +import org.onosproject.net.meter.Band; +import org.onosproject.net.meter.Meter; +import org.onosproject.net.meter.MeterId; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFMeterFlags; +import org.projectfloodlight.openflow.protocol.OFMeterMod; +import org.projectfloodlight.openflow.protocol.OFMeterModCommand; +import org.projectfloodlight.openflow.protocol.meterband.OFMeterBand; +import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDrop; +import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDscpRemark; +import org.slf4j.Logger; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Builder for a meter modification. + */ +public final class MeterModBuilder { + + private final Logger log = getLogger(getClass()); + + private final long xid; + private final OFFactory factory; + private Meter.Unit unit = Meter.Unit.KB_PER_SEC; + private boolean burst = false; + private Long id; + private Collection bands; + + public MeterModBuilder(long xid, OFFactory factory) { + this.xid = xid; + this.factory = factory; + } + + public static MeterModBuilder builder(long xid, OFFactory factory) { + return new MeterModBuilder(xid, factory); + } + + public MeterModBuilder withRateUnit(Meter.Unit unit) { + this.unit = unit; + return this; + } + + public MeterModBuilder burst() { + this.burst = true; + return this; + } + + public MeterModBuilder withId(MeterId meterId) { + this.id = meterId.id(); + return this; + } + + public MeterModBuilder withBands(Collection bands) { + this.bands = bands; + return this; + } + + public OFMeterMod add() { + validate(); + OFMeterMod.Builder builder = builderMeterMod(); + builder.setCommand(OFMeterModCommand.ADD.ordinal()); + return builder.build(); + } + + public OFMeterMod remove() { + validate(); + OFMeterMod.Builder builder = builderMeterMod(); + builder.setCommand(OFMeterModCommand.DELETE.ordinal()); + return builder.build(); + } + + public OFMeterMod modify() { + validate(); + OFMeterMod.Builder builder = builderMeterMod(); + builder.setCommand(OFMeterModCommand.MODIFY.ordinal()); + return builder.build(); + } + + private OFMeterMod.Builder builderMeterMod() { + OFMeterMod.Builder builder = factory.buildMeterMod(); + int flags = 0; + if (burst) { + // covering loxi short comings. + flags |= 1 << OFMeterFlags.BURST.ordinal(); + } + switch (unit) { + case PKTS_PER_SEC: + flags |= 1 << OFMeterFlags.PKTPS.ordinal(); + break; + case KB_PER_SEC: + flags |= 1 << OFMeterFlags.KBPS.ordinal(); + break; + default: + log.warn("Unknown unit type {}", unit); + } + //FIXME: THIS WILL CHANGE IN OF1.4 to setBands. + builder.setMeters(buildBands()); + builder.setFlags(flags) + .setMeterId(id) + .setXid(xid); + return builder; + } + + private List buildBands() { + return bands.stream().map(b -> { + switch (b.type()) { + case DROP: + OFMeterBandDrop.Builder dropBuilder = + factory.meterBands().buildDrop(); + if (burst) { + dropBuilder.setBurstSize(b.burst()); + } + dropBuilder.setRate(b.rate()); + return dropBuilder.build(); + case REMARK: + OFMeterBandDscpRemark.Builder remarkBand = + factory.meterBands().buildDscpRemark(); + if (burst) { + remarkBand.setBurstSize(b.burst()); + } + remarkBand.setRate(b.rate()); + remarkBand.setPrecLevel(b.dropPrecedence()); + return remarkBand.build(); + default: + log.warn("Unknown band type {}", b.type()); + return null; + } + }).filter(value -> value != null).collect(Collectors.toList()); + } + + private void validate() { + checkNotNull(id, "id cannot be null"); + checkNotNull(bands, "Must have bands"); + checkArgument(bands.size() > 0, "Must have at lease one band"); + } +} diff --git a/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterStatsCollector.java b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterStatsCollector.java new file mode 100644 index 00000000..83dc6458 --- /dev/null +++ b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterStatsCollector.java @@ -0,0 +1,103 @@ +/* + * 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.provider.of.meter.impl; + +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.util.Timer; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFMeterStatsRequest; +import org.slf4j.Logger; + +import java.util.concurrent.TimeUnit; + +import static org.slf4j.LoggerFactory.getLogger; + +/* + * Sends Meter Stats Request and collect the Meter statistics with a time interval. + */ +public class MeterStatsCollector implements TimerTask { + + private final HashedWheelTimer timer = Timer.getTimer(); + private final OpenFlowSwitch sw; + private final Logger log = getLogger(getClass()); + private final int refreshInterval; + + private Timeout timeout; + + private boolean stopTimer = false; + + /** + * Creates a GroupStatsCollector object. + * + * @param sw Open Flow switch + * @param interval time interval for collecting group statistic + */ + public MeterStatsCollector(OpenFlowSwitch sw, int interval) { + this.sw = sw; + this.refreshInterval = interval; + } + + @Override + public void run(Timeout timeout) throws Exception { + log.trace("Collecting stats for {}", sw.getStringId()); + + sendMeterStatistic(); + + if (!this.stopTimer) { + log.trace("Scheduling stats collection in {} seconds for {}", + this.refreshInterval, this.sw.getStringId()); + timeout.getTimer().newTimeout(this, refreshInterval, + TimeUnit.SECONDS); + } + } + + private void sendMeterStatistic() { + if (log.isTraceEnabled()) { + log.trace("sendMeterStatistics {}:{}", sw.getStringId(), sw.getRole()); + } + if (sw.getRole() != RoleState.MASTER) { + return; + } + + OFMeterStatsRequest.Builder builder = + sw.factory().buildMeterStatsRequest(); + builder.setXid(0).setMeterId(0xFFFFFFFF); + + sw.sendMsg(builder.build()); + + } + + /** + * Starts the collector. + */ + public void start() { + log.info("Starting Meter Stats collection thread for {}", sw.getStringId()); + timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS); + } + + /** + * Stops the collector. + */ + public void stop() { + log.info("Stopping Meter Stats collection thread for {}", sw.getStringId()); + this.stopTimer = true; + timeout.cancel(); + } +} diff --git a/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java new file mode 100644 index 00000000..f5a777be --- /dev/null +++ b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java @@ -0,0 +1,393 @@ +/* + * 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.provider.of.meter.impl; + + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalCause; +import com.google.common.cache.RemovalNotification; +import com.google.common.collect.Maps; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onosproject.core.CoreService; +import org.onosproject.net.meter.Band; +import org.onosproject.net.meter.DefaultBand; +import org.onosproject.net.meter.DefaultMeter; +import org.onosproject.net.meter.Meter; +import org.onosproject.net.meter.MeterFailReason; +import org.onosproject.net.meter.MeterId; +import org.onosproject.net.meter.MeterOperation; +import org.onosproject.net.meter.MeterOperations; +import org.onosproject.net.meter.MeterProvider; +import org.onosproject.net.meter.MeterProviderRegistry; +import org.onosproject.net.meter.MeterProviderService; +import org.onosproject.net.meter.MeterState; +import org.onosproject.net.DeviceId; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowEventListener; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitchListener; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFErrorMsg; +import org.projectfloodlight.openflow.protocol.OFErrorType; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFMeterBandStats; +import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply; +import org.projectfloodlight.openflow.protocol.OFMeterStats; +import org.projectfloodlight.openflow.protocol.OFMeterStatsReply; +import org.projectfloodlight.openflow.protocol.OFPortStatus; +import org.projectfloodlight.openflow.protocol.OFStatsReply; +import org.projectfloodlight.openflow.protocol.OFStatsType; +import org.projectfloodlight.openflow.protocol.OFVersion; +import org.projectfloodlight.openflow.protocol.errormsg.OFMeterModFailedErrorMsg; +import org.slf4j.Logger; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Provider which uses an OpenFlow controller to handle meters. + */ +@Component(immediate = true, enabled = true) +public class OpenFlowMeterProvider extends AbstractProvider implements MeterProvider { + + + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenFlowController controller; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected MeterProviderRegistry providerRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + private MeterProviderService providerService; + + private static final AtomicLong XID_COUNTER = new AtomicLong(1); + + static final int POLL_INTERVAL = 10; + static final long TIMEOUT = 30; + + private Cache pendingOperations; + + + private InternalMeterListener listener = new InternalMeterListener(); + private Map collectors = Maps.newHashMap(); + + /** + * Creates a OpenFlow meter provider. + */ + public OpenFlowMeterProvider() { + super(new ProviderId("of", "org.onosproject.provider.meter")); + } + + @Activate + public void activate() { + providerService = providerRegistry.register(this); + + pendingOperations = CacheBuilder.newBuilder() + .expireAfterWrite(TIMEOUT, TimeUnit.SECONDS) + .removalListener((RemovalNotification notification) -> { + if (notification.getCause() == RemovalCause.EXPIRED) { + providerService.meterOperationFailed(notification.getValue(), + MeterFailReason.TIMEOUT); + } + }).build(); + + controller.addEventListener(listener); + controller.addListener(listener); + + controller.getSwitches().forEach((sw -> createStatsCollection(sw))); + } + + @Deactivate + public void deactivate() { + providerRegistry.unregister(this); + controller.removeEventListener(listener); + controller.removeListener(listener); + providerService = null; + } + + @Override + public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) { + Dpid dpid = Dpid.dpid(deviceId.uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + if (sw == null) { + log.error("Unknown device {}", deviceId); + meterOps.operations().forEach(op -> + providerService.meterOperationFailed(op, + MeterFailReason.UNKNOWN_DEVICE) + ); + return; + } + + meterOps.operations().forEach(op -> performOperation(sw, op)); + } + + @Override + public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) { + Dpid dpid = Dpid.dpid(deviceId.uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + if (sw == null) { + log.error("Unknown device {}", deviceId); + providerService.meterOperationFailed(meterOp, + MeterFailReason.UNKNOWN_DEVICE); + return; + } + + performOperation(sw, meterOp); + + } + + private void performOperation(OpenFlowSwitch sw, MeterOperation op) { + + pendingOperations.put(op.meter().id().id(), op); + + + Meter meter = op.meter(); + MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory()); + if (meter.isBurst()) { + builder.burst(); + } + builder.withBands(meter.bands()) + .withId(meter.id()) + .withRateUnit(meter.unit()); + + switch (op.type()) { + case ADD: + sw.sendMsg(builder.add()); + break; + case REMOVE: + sw.sendMsg(builder.remove()); + break; + case MODIFY: + sw.sendMsg(builder.modify()); + break; + default: + log.warn("Unknown Meter command {}; not sending anything", + op.type()); + providerService.meterOperationFailed(op, + MeterFailReason.UNKNOWN_COMMAND); + } + + } + + private void createStatsCollection(OpenFlowSwitch sw) { + if (isMeterSupported(sw)) { + MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL); + msc.start(); + collectors.put(new Dpid(sw.getId()), msc); + } + } + + private boolean isMeterSupported(OpenFlowSwitch sw) { + if (sw.factory().getVersion() == OFVersion.OF_10 || + sw.factory().getVersion() == OFVersion.OF_11 || + sw.factory().getVersion() == OFVersion.OF_12) { + return false; + } + + return true; + } + + private void pushMeterStats(Dpid dpid, OFStatsReply msg) { + DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid)); + + if (msg.getStatsType() == OFStatsType.METER) { + OFMeterStatsReply reply = (OFMeterStatsReply) msg; + Collection meters = buildMeters(deviceId, reply.getEntries()); + //TODO do meter accounting here. + providerService.pushMeterMetrics(deviceId, meters); + } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) { + OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg; + // FIXME: Map meters = collectMeters(deviceId, reply); + } + + } + + private Map collectMeters(DeviceId deviceId, + OFMeterConfigStatsReply reply) { + return Maps.newHashMap(); + //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect + } + + private Collection buildMeters(DeviceId deviceId, + List entries) { + return entries.stream().map(stat -> { + DefaultMeter.Builder builder = DefaultMeter.builder(); + Collection bands = buildBands(stat.getBandStats()); + builder.forDevice(deviceId) + .withId(MeterId.meterId(stat.getMeterId())) + //FIXME: need to encode appId in meter id, but that makes + // things a little annoying for debugging + .fromApp(coreService.getAppId("org.onosproject.core")) + .withBands(bands); + DefaultMeter meter = builder.build(); + meter.setState(MeterState.ADDED); + meter.setLife(stat.getDurationSec()); + meter.setProcessedBytes(stat.getByteInCount().getValue()); + meter.setProcessedPackets(stat.getPacketInCount().getValue()); + meter.setReferenceCount(stat.getFlowCount()); + + // marks the meter as seen on the dataplane + pendingOperations.invalidate(stat.getMeterId()); + return meter; + }).collect(Collectors.toSet()); + } + + private Collection buildBands(List bandStats) { + return bandStats.stream().map(stat -> { + DefaultBand band = DefaultBand.builder().build(); + band.setBytes(stat.getByteBandCount().getValue()); + band.setPackets(stat.getPacketBandCount().getValue()); + return band; + }).collect(Collectors.toSet()); + } + + private void signalMeterError(OFMeterModFailedErrorMsg meterError, + MeterOperation op) { + switch (meterError.getCode()) { + case UNKNOWN: + providerService.meterOperationFailed(op, + MeterFailReason.UNKNOWN_DEVICE); + break; + case METER_EXISTS: + providerService.meterOperationFailed(op, + MeterFailReason.EXISTING_METER); + break; + case INVALID_METER: + providerService.meterOperationFailed(op, + MeterFailReason.INVALID_METER); + break; + case UNKNOWN_METER: + providerService.meterOperationFailed(op, + MeterFailReason.UNKNOWN); + break; + case BAD_COMMAND: + providerService.meterOperationFailed(op, + MeterFailReason.UNKNOWN_COMMAND); + break; + case BAD_FLAGS: + providerService.meterOperationFailed(op, + MeterFailReason.UNKNOWN_FLAGS); + break; + case BAD_RATE: + providerService.meterOperationFailed(op, + MeterFailReason.BAD_RATE); + break; + case BAD_BURST: + providerService.meterOperationFailed(op, + MeterFailReason.BAD_BURST); + break; + case BAD_BAND: + providerService.meterOperationFailed(op, + MeterFailReason.BAD_BAND); + break; + case BAD_BAND_VALUE: + providerService.meterOperationFailed(op, + MeterFailReason.BAD_BAND_VALUE); + break; + case OUT_OF_METERS: + providerService.meterOperationFailed(op, + MeterFailReason.OUT_OF_METERS); + break; + case OUT_OF_BANDS: + providerService.meterOperationFailed(op, + MeterFailReason.OUT_OF_BANDS); + break; + default: + providerService.meterOperationFailed(op, + MeterFailReason.UNKNOWN); + } + } + + private class InternalMeterListener + implements OpenFlowSwitchListener, OpenFlowEventListener { + @Override + public void handleMessage(Dpid dpid, OFMessage msg) { + switch (msg.getType()) { + case STATS_REPLY: + pushMeterStats(dpid, (OFStatsReply) msg); + break; + case ERROR: + OFErrorMsg error = (OFErrorMsg) msg; + if (error.getErrType() == OFErrorType.METER_MOD_FAILED) { + MeterOperation op = + pendingOperations.getIfPresent(error.getXid()); + pendingOperations.invalidate(error.getXid()); + if (op == null) { + log.warn("Unknown Meter operation failed {}", error); + } else { + OFMeterModFailedErrorMsg meterError = + (OFMeterModFailedErrorMsg) error; + signalMeterError(meterError, op); + } + } + break; + default: + break; + } + + } + + @Override + public void switchAdded(Dpid dpid) { + createStatsCollection(controller.getSwitch(dpid)); + } + + @Override + public void switchRemoved(Dpid dpid) { + MeterStatsCollector msc = collectors.remove(dpid); + if (msc != null) { + msc.stop(); + } + } + + @Override + public void switchChanged(Dpid dpid) { + + } + + @Override + public void portChanged(Dpid dpid, OFPortStatus status) { + + } + + @Override + public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) { + + } + } + + + +} diff --git a/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/package-info.java b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/package-info.java new file mode 100644 index 00000000..9515712c --- /dev/null +++ b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Provider that uses OpenFlow controller as a means of device metering management. + */ +package org.onosproject.provider.of.meter.impl; \ No newline at end of file diff --git a/framework/src/onos/providers/openflow/meter/src/test/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProviderTest.java b/framework/src/onos/providers/openflow/meter/src/test/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProviderTest.java new file mode 100644 index 00000000..0c5d5389 --- /dev/null +++ b/framework/src/onos/providers/openflow/meter/src/test/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProviderTest.java @@ -0,0 +1,22 @@ +/* + * 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.provider.of.meter.impl; + + +public class OpenFlowMeterProviderTest { + + +} \ No newline at end of file diff --git a/framework/src/onos/providers/openflow/packet/pom.xml b/framework/src/onos/providers/openflow/packet/pom.xml new file mode 100644 index 00000000..f7f62d8e --- /dev/null +++ b/framework/src/onos/providers/openflow/packet/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + + org.onosproject + onos-of-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-of-provider-packet + bundle + + ONOS OpenFlow protocol packet provider + + diff --git a/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java new file mode 100644 index 00000000..6d153103 --- /dev/null +++ b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java @@ -0,0 +1,99 @@ +/* + * Copyright 2014-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.provider.of.packet.impl; + +import org.onlab.packet.DeserializationException; +import org.onlab.packet.Ethernet; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instruction.Type; +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; +import org.onosproject.net.packet.DefaultPacketContext; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.openflow.controller.OpenFlowPacketContext; +import org.projectfloodlight.openflow.types.OFPort; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Packet context used with the OpenFlow providers. + */ +public class OpenFlowCorePacketContext extends DefaultPacketContext { + + private static final Logger log = LoggerFactory.getLogger(OpenFlowCorePacketContext.class); + + private final OpenFlowPacketContext ofPktCtx; + + /** + * Creates a new OpenFlow core packet context. + * + * @param time creation time + * @param inPkt inbound packet + * @param outPkt outbound packet + * @param block whether the context is blocked or not + * @param ofPktCtx OpenFlow packet context + */ + protected OpenFlowCorePacketContext(long time, InboundPacket inPkt, + OutboundPacket outPkt, boolean block, + OpenFlowPacketContext ofPktCtx) { + super(time, inPkt, outPkt, block); + this.ofPktCtx = ofPktCtx; + } + + @Override + public void send() { + if (!this.block()) { + if (outPacket() == null) { + sendPacket(null); + } else { + try { + Ethernet eth = Ethernet.deserializer() + .deserialize(outPacket().data().array(), 0, + outPacket().data().array().length); + sendPacket(eth); + } catch (DeserializationException e) { + log.warn("Unable to deserialize packet"); + } + } + } + } + + private void sendPacket(Ethernet eth) { + List ins = treatmentBuilder().build().allInstructions(); + OFPort p = null; + //TODO: support arbitrary list of treatments must be supported in ofPacketContext + for (Instruction i : ins) { + if (i.type() == Type.OUTPUT) { + p = buildPort(((OutputInstruction) i).port()); + break; //for now... + } + } + if (eth == null) { + ofPktCtx.build(p); + } else { + ofPktCtx.build(eth, p); + } + ofPktCtx.send(); + } + + private OFPort buildPort(PortNumber port) { + return OFPort.of((int) port.toLong()); + } + +} diff --git a/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java new file mode 100644 index 00000000..dc79feff --- /dev/null +++ b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java @@ -0,0 +1,176 @@ +/* + * Copyright 2014-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.provider.of.packet.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; +import org.onosproject.net.packet.DefaultInboundPacket; +import org.onosproject.net.packet.DefaultOutboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketProvider; +import org.onosproject.net.packet.PacketProviderRegistry; +import org.onosproject.net.packet.PacketProviderService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowPacketContext; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.PacketListener; +import org.projectfloodlight.openflow.protocol.OFPacketOut; +import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.action.OFAction; +import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10; +import org.projectfloodlight.openflow.types.OFBufferId; +import org.projectfloodlight.openflow.types.OFPort; +import org.slf4j.Logger; + +import java.nio.ByteBuffer; +import java.util.Collections; + +import static org.slf4j.LoggerFactory.getLogger; + + +/** + * Provider which uses an OpenFlow controller to detect network + * infrastructure links. + */ +@Component(immediate = true) +public class OpenFlowPacketProvider extends AbstractProvider implements PacketProvider { + + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketProviderRegistry providerRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenFlowController controller; + + private PacketProviderService providerService; + + private final InternalPacketProvider listener = new InternalPacketProvider(); + + /** + * Creates an OpenFlow link provider. + */ + public OpenFlowPacketProvider() { + super(new ProviderId("of", "org.onosproject.provider.openflow")); + } + + @Activate + public void activate() { + providerService = providerRegistry.register(this); + controller.addPacketListener(20, listener); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + providerRegistry.unregister(this); + controller.removePacketListener(listener); + providerService = null; + log.info("Stopped"); + } + + @Override + public void emit(OutboundPacket packet) { + DeviceId devId = packet.sendThrough(); + String scheme = devId.toString().split(":")[0]; + + if (!scheme.equals(this.id().scheme())) { + throw new IllegalArgumentException( + "Don't know how to handle Device with scheme " + scheme); + } + + Dpid dpid = Dpid.dpid(devId.uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + if (sw == null) { + log.warn("Device {} isn't available?", devId); + return; + } + + //Ethernet eth = new Ethernet(); + //eth.deserialize(packet.data().array(), 0, packet.data().array().length); + OFPortDesc p = null; + for (Instruction inst : packet.treatment().allInstructions()) { + if (inst.type().equals(Instruction.Type.OUTPUT)) { + p = portDesc(((OutputInstruction) inst).port()); + OFPacketOut po = packetOut(sw, packet.data().array(), p.getPortNo()); + sw.sendMsg(po); + } + } + + } + + private OFPortDesc portDesc(PortNumber port) { + OFPortDesc.Builder builder = OFFactoryVer10.INSTANCE.buildPortDesc(); + builder.setPortNo(OFPort.of((int) port.toLong())); + + return builder.build(); + } + + private OFPacketOut packetOut(OpenFlowSwitch sw, byte[] eth, OFPort out) { + OFPacketOut.Builder builder = sw.factory().buildPacketOut(); + OFAction act = sw.factory().actions() + .buildOutput() + .setPort(out) + .build(); + return builder + .setBufferId(OFBufferId.NO_BUFFER) + .setInPort(OFPort.CONTROLLER) + .setActions(Collections.singletonList(act)) + .setData(eth) + .build(); + } + + /** + * Internal Packet Provider implementation. + * + */ + private class InternalPacketProvider implements PacketListener { + + @Override + public void handlePacket(OpenFlowPacketContext pktCtx) { + DeviceId id = DeviceId.deviceId(Dpid.uri(pktCtx.dpid().value())); + + DefaultInboundPacket inPkt = new DefaultInboundPacket( + new ConnectPoint(id, PortNumber.portNumber(pktCtx.inPort())), + pktCtx.parsed(), ByteBuffer.wrap(pktCtx.unparsed())); + + DefaultOutboundPacket outPkt = null; + if (!pktCtx.isBuffered()) { + outPkt = new DefaultOutboundPacket(id, null, + ByteBuffer.wrap(pktCtx.unparsed())); + } + + OpenFlowCorePacketContext corePktCtx = + new OpenFlowCorePacketContext(System.currentTimeMillis(), + inPkt, outPkt, pktCtx.isHandled(), pktCtx); + providerService.processPacket(corePktCtx); + } + + } + + +} diff --git a/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/package-info.java b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/package-info.java new file mode 100644 index 00000000..dd1130c0 --- /dev/null +++ b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2014 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. + */ + +/** + * Provider that uses OpenFlow controller as a means of intercepting and + * emitting packets. + */ +package org.onosproject.provider.of.packet.impl; diff --git a/framework/src/onos/providers/openflow/packet/src/test/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProviderTest.java b/framework/src/onos/providers/openflow/packet/src/test/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProviderTest.java new file mode 100644 index 00000000..5fded926 --- /dev/null +++ b/framework/src/onos/providers/openflow/packet/src/test/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProviderTest.java @@ -0,0 +1,431 @@ +/* + * Copyright 2014-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.provider.of.packet.impl; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.ARP; +import org.onlab.packet.Ethernet; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.packet.DefaultOutboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketProvider; +import org.onosproject.net.packet.PacketProviderRegistry; +import org.onosproject.net.packet.PacketProviderService; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext; +import org.onosproject.openflow.controller.Dpid; +import org.onosproject.openflow.controller.OpenFlowController; +import org.onosproject.openflow.controller.OpenFlowEventListener; +import org.onosproject.openflow.controller.OpenFlowPacketContext; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.OpenFlowSwitchListener; +import org.onosproject.openflow.controller.PacketListener; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFFactory; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPacketIn; +import org.projectfloodlight.openflow.protocol.OFPacketInReason; +import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10; +import org.projectfloodlight.openflow.types.MacAddress; +import org.projectfloodlight.openflow.types.OFBufferId; +import org.projectfloodlight.openflow.types.OFPort; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.*; + + +public class OpenFlowPacketProviderTest { + + private static final int PN1 = 100; + private static final int PN2 = 200; + private static final int PN3 = 300; + private static final short VLANID = (short) 100; + + private static final DeviceId DID = DeviceId.deviceId("of:1"); + private static final DeviceId DID_MISSING = DeviceId.deviceId("of:2"); + private static final DeviceId DID_WRONG = DeviceId.deviceId("test:1"); + private static final PortNumber P1 = PortNumber.portNumber(PN1); + private static final PortNumber P2 = PortNumber.portNumber(PN2); + private static final PortNumber P3 = PortNumber.portNumber(PN3); + + private static final Instruction INST1 = Instructions.createOutput(P1); + private static final Instruction INST2 = Instructions.createOutput(P2); + private static final Instruction INST3 = Instructions.createOutput(P3); + + private static final OFPortDesc PD1 = portDesc(PN1); + private static final OFPortDesc PD2 = portDesc(PN2); + + private static final List PLIST = Lists.newArrayList(PD1, PD2); + private static final TrafficTreatment TR = treatment(INST1, INST2); + private static final TrafficTreatment TR_MISSING = treatment(INST1, INST3); + + private static final byte[] ANY = new byte [] {0, 0, 0, 0}; + + private final OpenFlowPacketProvider provider = new OpenFlowPacketProvider(); + private final TestPacketRegistry registry = new TestPacketRegistry(); + private final TestController controller = new TestController(); + + private final TestOpenFlowSwitch sw = new TestOpenFlowSwitch(); + + @Before + public void startUp() { + provider.providerRegistry = registry; + provider.controller = controller; + provider.activate(); + assertNotNull("listener should be registered", registry.listener); + } + + @After + public void teardown() { + provider.deactivate(); + assertNull("listeners shouldn't be registered", registry.listener); + provider.controller = null; + provider.providerRegistry = null; + } + + @Test(expected = IllegalArgumentException.class) + public void wrongScheme() { + sw.setRole(RoleState.MASTER); + OutboundPacket schemeFailPkt = outPacket(DID_WRONG, TR, null); + provider.emit(schemeFailPkt); + assertEquals("message sent incorrectly", 0, sw.sent.size()); + } + + @Test + public void emit() { + + MacAddress mac1 = MacAddress.of("00:00:00:11:00:01"); + MacAddress mac2 = MacAddress.of("00:00:00:22:00:02"); + + ARP arp = new ARP(); + arp.setSenderProtocolAddress(ANY) + .setSenderHardwareAddress(mac1.getBytes()) + .setTargetHardwareAddress(mac2.getBytes()) + .setTargetProtocolAddress(ANY) + .setHardwareType((short) 0) + .setProtocolType((short) 0) + .setHardwareAddressLength((byte) 6) + .setProtocolAddressLength((byte) 4) + .setOpCode((byte) 0); + + Ethernet eth = new Ethernet(); + eth.setVlanID(VLANID) + .setEtherType(Ethernet.TYPE_ARP) + .setSourceMACAddress("00:00:00:11:00:01") + .setDestinationMACAddress("00:00:00:22:00:02") + .setPayload(arp); + + //the should-be working setup. + OutboundPacket passPkt = outPacket(DID, TR, eth); + sw.setRole(RoleState.MASTER); + provider.emit(passPkt); + assertEquals("invalid switch", sw, controller.current); + assertEquals("message not sent", PLIST.size(), sw.sent.size()); + sw.sent.clear(); + + //wrong Role + //sw.setRole(RoleState.SLAVE); + //provider.emit(passPkt); + //assertEquals("invalid switch", sw, controller.current); + //assertEquals("message sent incorrectly", 0, sw.sent.size()); + + //sw.setRole(RoleState.MASTER); + + //missing switch + OutboundPacket swFailPkt = outPacket(DID_MISSING, TR, eth); + provider.emit(swFailPkt); + assertNull("invalid switch", controller.current); + assertEquals("message sent incorrectly", 0, sw.sent.size()); + + //to missing port + //OutboundPacket portFailPkt = outPacket(DID, TR_MISSING, eth); + //provider.emit(portFailPkt); + //assertEquals("extra message sent", 1, sw.sent.size()); + + } + + @Test + public void handlePacket() { + OFPacketIn pkt = sw.factory().buildPacketIn() + .setBufferId(OFBufferId.NO_BUFFER) + .setInPort(OFPort.NO_MASK) + .setReason(OFPacketInReason.INVALID_TTL) + .build(); + + controller.processPacket(null, pkt); + assertNotNull("message unprocessed", registry.ctx); + + } + + private static OFPortDesc portDesc(int port) { + OFPortDesc.Builder builder = OFFactoryVer10.INSTANCE.buildPortDesc(); + builder.setPortNo(OFPort.of(port)); + + return builder.build(); + } + + private static TrafficTreatment treatment(Instruction ... insts) { + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); + for (Instruction i : insts) { + builder.add(i); + } + return builder.build(); + } + + private static OutboundPacket outPacket( + DeviceId d, TrafficTreatment t, Ethernet e) { + ByteBuffer buf = null; + if (e != null) { + buf = ByteBuffer.wrap(e.serialize()); + } + return new DefaultOutboundPacket(d, t, buf); + } + + private class TestPacketRegistry implements PacketProviderRegistry { + + PacketProvider listener = null; + PacketContext ctx = null; + + @Override + public PacketProviderService register(PacketProvider provider) { + listener = provider; + return new TestPacketProviderService(); + } + + @Override + public void unregister(PacketProvider provider) { + listener = null; + } + + @Override + public Set getProviders() { + return Sets.newHashSet(listener.id()); + } + + private class TestPacketProviderService implements PacketProviderService { + + @Override + public PacketProvider provider() { + return null; + } + + @Override + public void processPacket(PacketContext context) { + ctx = context; + } + + } + } + + private class TestController implements OpenFlowController { + + PacketListener pktListener; + OpenFlowSwitch current; + + @Override + public Iterable getSwitches() { + return null; + } + + @Override + public Iterable getMasterSwitches() { + return null; + } + + @Override + public Iterable getEqualSwitches() { + return null; + } + + @Override + public OpenFlowSwitch getSwitch(Dpid dpid) { + if (dpid.equals(Dpid.dpid(DID.uri()))) { + current = sw; + } else { + current = null; + } + return current; + } + + @Override + public OpenFlowSwitch getMasterSwitch(Dpid dpid) { + return null; + } + + @Override + public OpenFlowSwitch getEqualSwitch(Dpid dpid) { + return null; + } + + @Override + public void addListener(OpenFlowSwitchListener listener) { + } + + @Override + public void removeListener(OpenFlowSwitchListener listener) { + } + + @Override + public void addPacketListener(int priority, PacketListener listener) { + pktListener = listener; + } + + @Override + public void removePacketListener(PacketListener listener) { + } + + @Override + public void addEventListener(OpenFlowEventListener listener) { + } + + @Override + public void removeEventListener(OpenFlowEventListener listener) { + } + + @Override + public void write(Dpid dpid, OFMessage msg) { + } + + @Override + public void processPacket(Dpid dpid, OFMessage msg) { + OpenFlowPacketContext pktCtx = + DefaultOpenFlowPacketContext. + packetContextFromPacketIn(sw, (OFPacketIn) msg); + pktListener.handlePacket(pktCtx); + } + + @Override + public void setRole(Dpid dpid, RoleState role) { + } + + } + + private class TestOpenFlowSwitch implements OpenFlowSwitch { + + RoleState state; + List sent = new ArrayList(); + OFFactory factory = OFFactoryVer10.INSTANCE; + + @Override + public void sendMsg(OFMessage msg) { + sent.add(msg); + } + + @Override + public void sendMsg(List msgs) { + } + + @Override + public void handleMessage(OFMessage fromSwitch) { + } + + @Override + public void setRole(RoleState role) { + state = role; + } + + @Override + public RoleState getRole() { + return state; + } + + @Override + public List getPorts() { + return PLIST; + } + + @Override + public OFFactory factory() { + return factory; + } + + @Override + public String getStringId() { + return null; + } + + @Override + public long getId() { + return 0; + } + + @Override + public String manufacturerDescription() { + return null; + } + + @Override + public String datapathDescription() { + return null; + } + + @Override + public String hardwareDescription() { + return null; + } + + @Override + public String softwareDescription() { + return null; + } + + @Override + public String serialNumber() { + return null; + } + + @Override + public boolean isConnected() { + return true; + } + + @Override + public void disconnectSwitch() { + } + + @Override + public void returnRoleReply(RoleState requested, RoleState reponse) { + } + @Override + public Device.Type deviceType() { + return Device.Type.SWITCH; + } + + @Override + public String channelId() { + return "1.2.3.4:1"; + } + + + } + +} diff --git a/framework/src/onos/providers/openflow/pom.xml b/framework/src/onos/providers/openflow/pom.xml new file mode 100644 index 00000000..99ff6649 --- /dev/null +++ b/framework/src/onos/providers/openflow/pom.xml @@ -0,0 +1,63 @@ + + + + 4.0.0 + + + org.onosproject + onos-providers + 1.3.0-SNAPSHOT + ../pom.xml + + + onos-of-providers + pom + + ONOS OpenFlow protocol adapters + + + device + packet + flow + group + meter + app + + + + + org.onosproject + onos-of-api + + + org.onosproject + onos-of-api + tests + test + + + + org.onosproject + onos-api + tests + test + + + + -- cgit 1.2.3-korg