blob: 621d16dee7d1bd5cb42d41c88c14bf5503aa1896 [file] [log] [blame]
Zayne Khouja31da33a2016-06-16 11:57:12 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Zayne Khouja31da33a2016-06-16 11:57:12 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.learningswitch;
17
18import com.google.common.collect.Maps;
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.onlab.packet.Ethernet;
25import org.onlab.packet.MacAddress;
26import org.onosproject.core.ApplicationId;
27import org.onosproject.core.CoreService;
28import org.onosproject.net.ConnectPoint;
29import org.onosproject.net.DeviceId;
30import org.onosproject.net.PortNumber;
31import org.onosproject.net.flow.DefaultFlowRule;
32import org.onosproject.net.flow.DefaultTrafficSelector;
33import org.onosproject.net.flow.DefaultTrafficTreatment;
34import org.onosproject.net.flow.FlowRule;
35import org.onosproject.net.flow.FlowRuleService;
36import org.onosproject.net.packet.PacketContext;
37import org.onosproject.net.packet.PacketPriority;
38import org.onosproject.net.packet.PacketProcessor;
39import org.onosproject.net.packet.PacketService;
40import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
43import java.util.Map;
44import java.util.Optional;
45
46/**
47 * Tutorial class used to help build a basic onos learning switch application.
48 * This class contains the solution to the learning switch tutorial. Change "enabled = false"
49 * to "enabled = true" below, to run the solution.
50 */
51@Component(immediate = true, enabled = false)
52public class LearningSwitchSolution {
53
54 // Instantiates the relevant services.
55 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
56 protected PacketService packetService;
57
58 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
59 protected FlowRuleService flowRuleService;
60
61 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
62 protected CoreService coreService;
63
64 private final Logger log = LoggerFactory.getLogger(getClass());
65
66 /*
67 * Defining macTables as a concurrent map allows multiple threads and packets to
68 * use the map without an issue.
69 */
70 protected Map<DeviceId, Map<MacAddress, PortNumber>> macTables = Maps.newConcurrentMap();
71 private ApplicationId appId;
72 private PacketProcessor processor;
73
74 /**
75 * Create a variable of the SwitchPacketProcessor class using the PacketProcessor defined above.
76 * Activates the app.
77 */
78 @Activate
79 protected void activate() {
80 log.info("Started");
81 appId = coreService.getAppId("org.onosproject.learningswitch"); //equal to the name shown in pom.xml file
82
Ray Milkeyc108a6b2017-08-23 15:23:50 -070083 processor = new SwitchPacketProcessor();
Zayne Khouja31da33a2016-06-16 11:57:12 -070084 packetService.addProcessor(processor, PacketProcessor.director(3));
85
86 /*
87 * Restricts packet types to IPV4 and ARP by only requesting those types.
88 */
89 packetService.requestPackets(DefaultTrafficSelector.builder()
90 .matchEthType(Ethernet.TYPE_IPV4).build(), PacketPriority.REACTIVE, appId, Optional.empty());
91 packetService.requestPackets(DefaultTrafficSelector.builder()
92 .matchEthType(Ethernet.TYPE_ARP).build(), PacketPriority.REACTIVE, appId, Optional.empty());
93 }
94
95 /**
96 * Deactivates the processor by removing it.
97 */
98 @Deactivate
99 protected void deactivate() {
100 log.info("Stopped");
101 packetService.removeProcessor(processor);
102 }
103
104 /**
105 * This class contains pseudo code that you must replace with your own code. Your job is to
106 * send the packet out the port previously learned for the destination MAC, if it
107 * exists. Otherwise flood the packet (to all ports).
108 */
Ray Milkeyc108a6b2017-08-23 15:23:50 -0700109 private class SwitchPacketProcessor implements PacketProcessor {
Zayne Khouja31da33a2016-06-16 11:57:12 -0700110 /**
111 * Learns the source port associated with the packet's DeviceId if it has not already been learned.
112 * Calls actLikeSwitch to process and send the packet.
113 * @param pc PacketContext object containing packet info
114 */
115 @Override
116 public void process(PacketContext pc) {
117 log.info(pc.toString());
118 initMacTable(pc.inPacket().receivedFrom());
119
120
121 // This is the basic flood all ports switch that is enabled.
122 //actLikeHub(pc);
123
124 /*
125 * This is the call to the actLikeSwitch method you will be creating. When
126 * you are ready to test it, uncomment the line below, and comment out the
127 * actLikeHub call above.
128 */
129 actLikeSwitch(pc);
130
131 }
132
133 /**
134 * Example method. Floods packet out of all switch ports.
135 *
136 * @param pc the PacketContext object passed through from activate() method
137 */
138 public void actLikeHub(PacketContext pc) {
139 pc.treatmentBuilder().setOutput(PortNumber.FLOOD);
140 pc.send();
141 }
142
143 /**
144 * Ensures packet is of required type. Obtain the PortNumber associated with the inPackets DeviceId.
145 * If this port has previously been learned (in initMacTable method) build a flow using the packet's
146 * out port, treatment, destination, and other properties. Send the flow to the learned out port.
147 * Otherwise, flood packet to all ports if out port is not learned.
148 *
149 * @param pc the PacketContext object passed through from activate() method
150 */
151 public void actLikeSwitch(PacketContext pc) {
152
153 /*
154 * Ensures the type of packet being processed is only of type IPV4 (not LLDP or BDDP). If it is not, return
155 * and do nothing with the packet. actLikeSwitch can only process IPV4 packets.
156 */
157 Short type = pc.inPacket().parsed().getEtherType();
158 if (type != Ethernet.TYPE_IPV4) {
159 return;
160 }
161
162 /*
163 * Learn the destination, source, and output port of the packet using a ConnectPoint and the
164 * associated macTable. If there is a known port associated with the packet's destination MAC Address,
165 * the output port will not be null.
166 */
167 ConnectPoint cp = pc.inPacket().receivedFrom();
168 Map<MacAddress, PortNumber> macTable = macTables.get(cp.deviceId());
169 MacAddress srcMac = pc.inPacket().parsed().getSourceMAC();
170 MacAddress dstMac = pc.inPacket().parsed().getDestinationMAC();
171 macTable.put(srcMac, cp.port());
172 PortNumber outPort = macTable.get(dstMac);
173
174 /*
175 * If port is known, set pc's out port to the packet's learned output port and construct a
176 * FlowRule using a source, destination, treatment and other properties. Send the FlowRule
177 * to the designated output port.
178 */
179 if (outPort != null) {
180 pc.treatmentBuilder().setOutput(outPort);
181 FlowRule fr = DefaultFlowRule.builder()
182 .withSelector(DefaultTrafficSelector.builder().matchEthDst(dstMac).build())
183 .withTreatment(DefaultTrafficTreatment.builder().setOutput(outPort).build())
184 .forDevice(cp.deviceId()).withPriority(PacketPriority.REACTIVE.priorityValue())
185 .makeTemporary(60)
186 .fromApp(appId).build();
187
188 flowRuleService.applyFlowRules(fr);
189 pc.send();
190 } else {
191 /*
192 * else, the output port has not been learned yet. Flood the packet to all ports using
193 * the actLikeHub method
194 */
195 actLikeHub(pc);
196 }
197 }
198
199 /**
200 * puts the ConnectPoint's device Id into the map macTables if it has not previously been added.
201 * @param cp ConnectPoint containing the required DeviceId for the map
202 */
203 private void initMacTable(ConnectPoint cp) {
204 macTables.putIfAbsent(cp.deviceId(), Maps.newConcurrentMap());
205
206 }
207 }
208}