Zayne Khouja
Committed by Gerrit Code Review

ONOS learning-switch tutorial

Change-Id: Id82a0886be5bbead60ea61dee30dd3ee12d20bbc
COMPILE_DEPS = [
'//lib:CORE_DEPS',
'//lib:slf4j-api',
'//lib:org.apache.felix.scr.annotations',
'//lib:junit',
]
osgi_jar_with_tests (
deps = COMPILE_DEPS,
)
onos_app (
title = 'Learning Switch Tutorial',
category = 'Tutorial',
url = 'http://onosproject.org',
description = 'Tutorial to help user create a learning switch',
)
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-apps</artifactId>
<version>1.7.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>onos-apps-learning-switch</artifactId>
<packaging>bundle</packaging>
<description>Learning Switch Tutorial Implementation</description>
<url>http://onosproject.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<onos.app.name>org.onosproject.learningswitch</onos.app.name>
<onos.app.title>Learning Switch</onos.app.title>
<onos.app.origin>ON.Lab</onos.app.origin>
<onos.app.category>Tutorial</onos.app.category>
<onos.app.url>http://onosproject.org</onos.app.url>
<onos.app.readme>Learning Switch Tutorial Implementation.</onos.app.readme>
</properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onlab-osgi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${project.version}</version>
<scope>test</scope>
<classifier>tests</classifier>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
<version>1.9.12</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
</project>
/*
* Copyright 2016 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.learningswitch;
import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketPriority;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Optional;
/**
* Tutorial class used to help build a basic onos learning switch application.
* This class contains the solution to the learning switch tutorial. Change "enabled = false"
* to "enabled = true" below, to run the solution.
*/
@Component(immediate = true, enabled = false)
public class LearningSwitchSolution {
// Instantiates the relevant services.
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
private final Logger log = LoggerFactory.getLogger(getClass());
/*
* Defining macTables as a concurrent map allows multiple threads and packets to
* use the map without an issue.
*/
protected Map<DeviceId, Map<MacAddress, PortNumber>> macTables = Maps.newConcurrentMap();
private ApplicationId appId;
private PacketProcessor processor;
/**
* Create a variable of the SwitchPacketProcessor class using the PacketProcessor defined above.
* Activates the app.
*/
@Activate
protected void activate() {
log.info("Started");
appId = coreService.getAppId("org.onosproject.learningswitch"); //equal to the name shown in pom.xml file
processor = new SwitchPacketProcesser();
packetService.addProcessor(processor, PacketProcessor.director(3));
/*
* Restricts packet types to IPV4 and ARP by only requesting those types.
*/
packetService.requestPackets(DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4).build(), PacketPriority.REACTIVE, appId, Optional.empty());
packetService.requestPackets(DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_ARP).build(), PacketPriority.REACTIVE, appId, Optional.empty());
}
/**
* Deactivates the processor by removing it.
*/
@Deactivate
protected void deactivate() {
log.info("Stopped");
packetService.removeProcessor(processor);
}
/**
* This class contains pseudo code that you must replace with your own code. Your job is to
* send the packet out the port previously learned for the destination MAC, if it
* exists. Otherwise flood the packet (to all ports).
*/
private class SwitchPacketProcesser implements PacketProcessor {
/**
* Learns the source port associated with the packet's DeviceId if it has not already been learned.
* Calls actLikeSwitch to process and send the packet.
* @param pc PacketContext object containing packet info
*/
@Override
public void process(PacketContext pc) {
log.info(pc.toString());
initMacTable(pc.inPacket().receivedFrom());
// This is the basic flood all ports switch that is enabled.
//actLikeHub(pc);
/*
* This is the call to the actLikeSwitch method you will be creating. When
* you are ready to test it, uncomment the line below, and comment out the
* actLikeHub call above.
*/
actLikeSwitch(pc);
}
/**
* Example method. Floods packet out of all switch ports.
*
* @param pc the PacketContext object passed through from activate() method
*/
public void actLikeHub(PacketContext pc) {
pc.treatmentBuilder().setOutput(PortNumber.FLOOD);
pc.send();
}
/**
* Ensures packet is of required type. Obtain the PortNumber associated with the inPackets DeviceId.
* If this port has previously been learned (in initMacTable method) build a flow using the packet's
* out port, treatment, destination, and other properties. Send the flow to the learned out port.
* Otherwise, flood packet to all ports if out port is not learned.
*
* @param pc the PacketContext object passed through from activate() method
*/
public void actLikeSwitch(PacketContext pc) {
/*
* Ensures the type of packet being processed is only of type IPV4 (not LLDP or BDDP). If it is not, return
* and do nothing with the packet. actLikeSwitch can only process IPV4 packets.
*/
Short type = pc.inPacket().parsed().getEtherType();
if (type != Ethernet.TYPE_IPV4) {
return;
}
/*
* Learn the destination, source, and output port of the packet using a ConnectPoint and the
* associated macTable. If there is a known port associated with the packet's destination MAC Address,
* the output port will not be null.
*/
ConnectPoint cp = pc.inPacket().receivedFrom();
Map<MacAddress, PortNumber> macTable = macTables.get(cp.deviceId());
MacAddress srcMac = pc.inPacket().parsed().getSourceMAC();
MacAddress dstMac = pc.inPacket().parsed().getDestinationMAC();
macTable.put(srcMac, cp.port());
PortNumber outPort = macTable.get(dstMac);
/*
* If port is known, set pc's out port to the packet's learned output port and construct a
* FlowRule using a source, destination, treatment and other properties. Send the FlowRule
* to the designated output port.
*/
if (outPort != null) {
pc.treatmentBuilder().setOutput(outPort);
FlowRule fr = DefaultFlowRule.builder()
.withSelector(DefaultTrafficSelector.builder().matchEthDst(dstMac).build())
.withTreatment(DefaultTrafficTreatment.builder().setOutput(outPort).build())
.forDevice(cp.deviceId()).withPriority(PacketPriority.REACTIVE.priorityValue())
.makeTemporary(60)
.fromApp(appId).build();
flowRuleService.applyFlowRules(fr);
pc.send();
} else {
/*
* else, the output port has not been learned yet. Flood the packet to all ports using
* the actLikeHub method
*/
actLikeHub(pc);
}
}
/**
* puts the ConnectPoint's device Id into the map macTables if it has not previously been added.
* @param cp ConnectPoint containing the required DeviceId for the map
*/
private void initMacTable(ConnectPoint cp) {
macTables.putIfAbsent(cp.deviceId(), Maps.newConcurrentMap());
}
}
}
/*
* Copyright 2016 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.learningswitch;
import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.Ethernet;
import org.onlab.packet.MacAddress;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.packet.PacketContext;
import org.onosproject.net.packet.PacketPriority;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Optional;
/**
* Tutorial class used to help build a basic onos learning switch application.
* Edit your code in the activate, deactivate, and actLikeSwitch methods.
*/
@Component(immediate = true)
public class LearningSwitchTutorial {
// Instantiates the relevant services.
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected PacketService packetService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected FlowRuleService flowRuleService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
private final Logger log = LoggerFactory.getLogger(getClass());
/*
* Defining macTables as a concurrent map allows multiple threads and packets to
* use the map without an issue.
*/
protected Map<DeviceId, Map<MacAddress, PortNumber>> macTables = Maps.newConcurrentMap();
private ApplicationId appId;
private PacketProcessor processor;
/**
* Create a variable of the SwitchPacketProcessor class using the PacketProcessor defined above.
* Activates the app.
*
* Create code to add a processor
*/
@Activate
protected void activate() {
log.info("Started");
appId = coreService.getAppId("org.onosproject.learningswitch"); //equal to the name shown in pom.xml file
//Create and processor and add it using packetService
/*
* Restricts packet types to IPV4 and ARP by only requesting those types
*/
packetService.requestPackets(DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4).build(), PacketPriority.REACTIVE, appId, Optional.empty());
packetService.requestPackets(DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_ARP).build(), PacketPriority.REACTIVE, appId, Optional.empty());
}
/**
* Deactivates the processor by removing it.
*
* Create code to remove the processor.
*/
@Deactivate
protected void deactivate() {
log.info("Stopped");
//Remove the processor
}
/**
* This class contains pseudo code that you must replace with your own code in actLikeSwitch. Your job is to
* send the packet out the port previously learned for the destination MAC. If it does not exist,
* flood the packet out (to all ports).
*/
private class SwitchPacketProcesser implements PacketProcessor {
/**
* Learns the source port associated with the packet's DeviceId if it has not already been learned.
* Calls actLikeSwitch to process and send the packet.
* @param pc PacketContext object containing packet info
*/
@Override
public void process(PacketContext pc) {
log.info(pc.toString());
/*
* Puts the packet's source's device Id into the map macTables if it has not previously been added.
* (learns the output port)
*/
ConnectPoint cp = pc.inPacket().receivedFrom();
macTables.putIfAbsent(cp.deviceId(), Maps.newConcurrentMap());
// This method simply floods all ports with the packet.
actLikeHub(pc);
/*
* This is the call to the actLikeSwitch method you will be creating. When
* you are ready to test it, uncomment the line below, and comment out the
* actLikeHub call above.
*
* NOTE: The perk of an actLikeSwitch method over actLikeHub is speed.
* FlowRule allows much faster processing.
*/
//actLikeSwitch(pc);
}
/**
* Example method. Floods packet out of all switch ports.
*
* @param pc the PacketContext object passed through from activate method
*/
public void actLikeHub(PacketContext pc) {
pc.treatmentBuilder().setOutput(PortNumber.FLOOD);
pc.send();
}
/**
* Ensures packet is of required type. Obtain the port number associated with the packet's source ID.
* If this port has previously been learned (in the process method) build a flow using the packet's
* out port, treatment, destination, and other properties. Send the flow to the learned out port.
* Otherwise, flood packet to all ports if out port has not been learned.
*
* @param pc the PacketContext object passed through from activate() method
*/
public void actLikeSwitch(PacketContext pc) {
/*
* Ensures the type of packet being processed is only of type IPV4 or ARP (not LLDP or BDDP).
* If it is not, return and do nothing with the packet. actLikeSwitch can only process
* IPV4 and ARP packets.
*/
Short type = pc.inPacket().parsed().getEtherType();
if (type != Ethernet.TYPE_IPV4 && type != Ethernet.TYPE_ARP) {
return;
}
/*
* Learn the destination, source, and output port of the packet using a ConnectPoint and the
* associated macTable. If there is a known port associated with the packet's destination MAC Address,
* the output port will not be null.
*/
//find the packets connect point
//save the macTables port value for the deviceID
//save the outPort as a variable
//PortNumber outPort = ...
/*
* If port is known, set output port to the packet's learned output port and construct a
* FlowRule using a source, destination, treatment and other properties. Send the FlowRule
* to the designated output port.
*/
//if outPort isn't null
//construct FlowRule
//FlowRule fr = ...
//send the packet
/*
* else, the output port has not been learned yet. Flood the packet to all ports using
* the actLikeHub method
*/
//else
// call actLikeHub method
}
}
}
/*
* Copyright 2016-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Learning Switch Tutorial
*/
package org.onosproject.learningswitch;
\ No newline at end of file