blob: 0d0dda9e00c6631f6ac1729ec570dd030dbeba79 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07002 * Copyright 2014 Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * 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
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * 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.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.fwd;
alshabib030111e2014-09-15 15:56:42 -070017
Jonathan Hartbc4a7932014-10-21 11:46:00 -070018import static org.slf4j.LoggerFactory.getLogger;
19
20import java.util.Dictionary;
21import java.util.Set;
22
alshabib030111e2014-09-15 15:56:42 -070023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
tomc16656f2014-10-15 18:30:31 -070026import org.apache.felix.scr.annotations.Modified;
27import org.apache.felix.scr.annotations.Property;
alshabib030111e2014-09-15 15:56:42 -070028import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harte8600eb2015-01-12 10:30:45 -080030import org.onlab.packet.Ethernet;
Brian O'Connorabafb502014-12-02 22:26:20 -080031import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
Jonathan Hart06ae79d2015-01-13 18:56:33 -080033import org.onosproject.net.Device;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.net.Host;
35import org.onosproject.net.HostId;
36import org.onosproject.net.Path;
37import org.onosproject.net.PortNumber;
Jonathan Hart06ae79d2015-01-13 18:56:33 -080038import org.onosproject.net.device.DeviceEvent;
39import org.onosproject.net.device.DeviceListener;
40import org.onosproject.net.device.DeviceService;
Brian O'Connorabafb502014-12-02 22:26:20 -080041import org.onosproject.net.flow.DefaultFlowRule;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.FlowRule;
45import org.onosproject.net.flow.FlowRuleService;
46import org.onosproject.net.flow.TrafficSelector;
47import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.host.HostService;
49import org.onosproject.net.packet.InboundPacket;
50import org.onosproject.net.packet.PacketContext;
51import org.onosproject.net.packet.PacketProcessor;
52import org.onosproject.net.packet.PacketService;
53import org.onosproject.net.topology.TopologyService;
tomc16656f2014-10-15 18:30:31 -070054import org.osgi.service.component.ComponentContext;
tomc370ebd2014-09-16 01:25:21 -070055import org.slf4j.Logger;
alshabib030111e2014-09-15 15:56:42 -070056
tomc370ebd2014-09-16 01:25:21 -070057/**
58 * Sample reactive forwarding application.
59 */
60@Component(immediate = true)
alshabib030111e2014-09-15 15:56:42 -070061public class ReactiveForwarding {
62
alshabibba5ac482014-10-02 17:15:20 -070063 private static final int TIMEOUT = 10;
alshabiba0e04982014-10-03 13:03:19 -070064 private static final int PRIORITY = 10;
Jonathan Hart06ae79d2015-01-13 18:56:33 -080065 private static final int PUNT_RULE_PRIORITY = 5;
alshabibba5ac482014-10-02 17:15:20 -070066
tomc370ebd2014-09-16 01:25:21 -070067 private final Logger log = getLogger(getClass());
68
alshabib030111e2014-09-15 15:56:42 -070069 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected TopologyService topologyService;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected PacketService packetService;
74
alshabib8aef1ad2014-09-15 17:47:31 -070075 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected HostService hostService;
77
alshabib7b2748f2014-09-16 20:21:11 -070078 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
79 protected FlowRuleService flowRuleService;
80
alshabib92c65ad2014-10-08 21:56:05 -070081 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart06ae79d2015-01-13 18:56:33 -080082 protected DeviceService deviceService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib92c65ad2014-10-08 21:56:05 -070085 protected CoreService coreService;
86
tomc370ebd2014-09-16 01:25:21 -070087 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -070088
alshabiba68eb962014-09-24 20:34:13 -070089 private ApplicationId appId;
90
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -080091 @Property(name = "packetOutOnly", boolValue = false,
92 label = "Enable packet-out only forwarding; default is false")
93 private boolean packetOutOnly = false;
tomc16656f2014-10-15 18:30:31 -070094
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +090095 @Property(name = "ipv6Forwarding", boolValue = false,
96 label = "Enable IPv6 forwarding; default is false")
97 private boolean ipv6Forwarding = false;
98
alshabib030111e2014-09-15 15:56:42 -070099 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900100 public void activate(ComponentContext context) {
Brian O'Connorabafb502014-12-02 22:26:20 -0800101 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart06ae79d2015-01-13 18:56:33 -0800102 deviceService.addListener(new InternalDeviceListener());
103 pushRules();
alshabibc274c902014-10-03 14:58:27 -0700104 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900105 readComponentConfiguration(context);
alshabiba68eb962014-09-24 20:34:13 -0700106 log.info("Started with Application ID {}", appId.id());
alshabib030111e2014-09-15 15:56:42 -0700107 }
108
109 @Deactivate
110 public void deactivate() {
alshabiba68eb962014-09-24 20:34:13 -0700111 flowRuleService.removeFlowRulesById(appId);
alshabib030111e2014-09-15 15:56:42 -0700112 packetService.removeProcessor(processor);
113 processor = null;
tomc370ebd2014-09-16 01:25:21 -0700114 log.info("Stopped");
alshabib030111e2014-09-15 15:56:42 -0700115 }
tomc370ebd2014-09-16 01:25:21 -0700116
tomc16656f2014-10-15 18:30:31 -0700117 @Modified
118 public void modified(ComponentContext context) {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900119 readComponentConfiguration(context);
120 }
121
122 /**
123 * Extracts properties from the component configuration context.
124 *
125 * @param context the component context
126 */
127 private void readComponentConfiguration(ComponentContext context) {
128 Dictionary<?, ?> properties = context.getProperties();
129 boolean packetOutOnlyEnabled = isPropertyEnabled(properties, "packetOutOnly");
130 if (packetOutOnly != packetOutOnlyEnabled) {
131 packetOutOnly = packetOutOnlyEnabled;
132 log.info("Configured. Packet-out only forwarding is {}",
133 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700134 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900135 boolean ipv6ForwardingEnabled = isPropertyEnabled(properties, "ipv6Forwarding");
136 if (ipv6Forwarding != ipv6ForwardingEnabled) {
137 ipv6Forwarding = ipv6ForwardingEnabled;
138 log.info("Configured. IPv6 forwarding is {}",
139 ipv6Forwarding ? "enabled" : "disabled");
140 }
141 }
142
143 /**
144 * Check property name is defined and set to true.
145 *
146 * @param properties properties to be looked up
147 * @param propertyName the name of the property to look up
148 * @return true when the propertyName is defined and set to true
149 */
150 private static boolean isPropertyEnabled(Dictionary<?, ?> properties, String propertyName) {
151 boolean enabled = false;
152 try {
153 String flag = (String) properties.get(propertyName);
154 if (flag != null) {
155 enabled = flag.equals("true");
156 }
157 } catch (ClassCastException e) {
158 // No propertyName defined.
159 enabled = false;
160 }
161 return enabled;
tomc16656f2014-10-15 18:30:31 -0700162 }
tomc370ebd2014-09-16 01:25:21 -0700163
164 /**
165 * Packet processor responsible for forwarding packets along their paths.
166 */
167 private class ReactivePacketProcessor implements PacketProcessor {
168
169 @Override
170 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700171 // Stop processing if the packet has been handled, since we
172 // can't do any more to it.
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800173 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700174 return;
175 }
tomdc95b8a2014-09-17 08:07:26 -0700176
tomc370ebd2014-09-16 01:25:21 -0700177 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700178 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700179
Jonathan Harte8600eb2015-01-12 10:30:45 -0800180 if (ethPkt == null) {
181 return;
182 }
183
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900184 // Bail if this is deemed to be a control packet.
185 if (isControlPacket(ethPkt)) {
186 return;
187 }
188
189 // Skip IPv6 multicast packet when IPv6 forward is disabled.
190 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800191 return;
192 }
193
tom642b2262014-09-17 13:52:55 -0700194 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700195
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700196 // Do not process link-local addresses in any way.
197 if (id.mac().isLinkLocal()) {
198 return;
199 }
200
tomc370ebd2014-09-16 01:25:21 -0700201 // Do we know who this is for? If not, flood and bail.
202 Host dst = hostService.getHost(id);
203 if (dst == null) {
204 flood(context);
205 return;
206 }
207
208 // Are we on an edge switch that our destination is on? If so,
209 // simply forward out to the destination and bail.
210 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700211 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
212 installRule(context, dst.location().port());
213 }
tomc370ebd2014-09-16 01:25:21 -0700214 return;
215 }
216
217 // Otherwise, get a set of paths that lead from here to the
218 // destination edge switch.
219 Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700220 pkt.receivedFrom().deviceId(),
221 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700222 if (paths.isEmpty()) {
223 // If there are no paths, flood and bail.
224 flood(context);
225 return;
226 }
227
228 // Otherwise, pick a path that does not lead back to where we
229 // came from; if no such path, flood and bail.
230 Path path = pickForwardPath(paths, pkt.receivedFrom().port());
231 if (path == null) {
tom642b2262014-09-17 13:52:55 -0700232 log.warn("Doh... don't know where to go... {} -> {} received on {}",
tomc16656f2014-10-15 18:30:31 -0700233 ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
234 pkt.receivedFrom());
tomc370ebd2014-09-16 01:25:21 -0700235 flood(context);
236 return;
237 }
238
239 // Otherwise forward and be done with it.
alshabib7b2748f2014-09-16 20:21:11 -0700240 installRule(context, path.src().port());
tomc370ebd2014-09-16 01:25:21 -0700241 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800242
243 }
244
245 // Indicates whether this is a control packet, e.g. LLDP, BDDP
246 private boolean isControlPacket(Ethernet eth) {
247 short type = eth.getEtherType();
248 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700249 }
250
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800251 // Indicated whether this is an IPv6 multicast packet.
252 private boolean isIpv6Multicast(Ethernet eth) {
253 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
254 }
255
tomc370ebd2014-09-16 01:25:21 -0700256 // Selects a path from the given set that does not lead back to the
257 // specified port.
258 private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) {
259 for (Path path : paths) {
260 if (!path.src().port().equals(notToPort)) {
261 return path;
262 }
263 }
264 return null;
265 }
266
tom642b2262014-09-17 13:52:55 -0700267 // Floods the specified packet if permissible.
tomc370ebd2014-09-16 01:25:21 -0700268 private void flood(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700269 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700270 context.inPacket().receivedFrom())) {
tom642b2262014-09-17 13:52:55 -0700271 packetOut(context, PortNumber.FLOOD);
tomc370ebd2014-09-16 01:25:21 -0700272 } else {
273 context.block();
274 }
275 }
276
tom642b2262014-09-17 13:52:55 -0700277 // Sends a packet out the specified port.
278 private void packetOut(PacketContext context, PortNumber portNumber) {
alshabib010c31d2014-09-26 10:01:12 -0700279 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700280 context.send();
281 }
282
283 // Install a rule forwarding the packet to the specified port.
284 private void installRule(PacketContext context, PortNumber portNumber) {
tom642b2262014-09-17 13:52:55 -0700285 // We don't yet support bufferids in the flowservice so packet out first.
286 packetOut(context, portNumber);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800287 if (!packetOutOnly) {
288 // Install the flow rule to handle this type of message from now on.
289 Ethernet inPkt = context.inPacket().parsed();
290 TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
291 builder.matchEthType(inPkt.getEtherType())
292 .matchEthSrc(inPkt.getSourceMAC())
293 .matchEthDst(inPkt.getDestinationMAC())
294 .matchInport(context.inPacket().receivedFrom().port());
alshabib7b2748f2014-09-16 20:21:11 -0700295
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800296 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
297 treat.setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700298
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800299 FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
300 builder.build(), treat.build(), PRIORITY, appId, TIMEOUT, false);
alshabib6eb438a2014-10-01 16:39:37 -0700301
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800302 flowRuleService.applyFlowRules(f);
303 }
tomc370ebd2014-09-16 01:25:21 -0700304 }
305
Jonathan Hart06ae79d2015-01-13 18:56:33 -0800306 /**
307 * Pushes flow rules to all devices.
308 */
309 private void pushRules() {
310 for (Device device : deviceService.getDevices()) {
311 pushRules(device);
312 }
313 }
314
315 /**
316 * Pushes flow rules to the device to receive packets that need
317 * to be processed.
318 *
319 * @param device the device to push the rules to
320 */
321 private synchronized void pushRules(Device device) {
322 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
323 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
324
325 // Get all IPv4 packets
326 sbuilder.matchEthType(Ethernet.TYPE_IPV4);
327 tbuilder.punt();
328 FlowRule flowArp =
329 new DefaultFlowRule(device.id(),
330 sbuilder.build(), tbuilder.build(),
331 PUNT_RULE_PRIORITY, appId, 0, true);
332
333 flowRuleService.applyFlowRules(flowArp);
334 }
335
336 public class InternalDeviceListener implements DeviceListener {
337
338 @Override
339 public void event(DeviceEvent event) {
340 Device device = event.subject();
341 switch (event.type()) {
342 case DEVICE_ADDED:
343 pushRules(device);
344 break;
345 case DEVICE_AVAILABILITY_CHANGED:
346 case DEVICE_SUSPENDED:
347 case DEVICE_UPDATED:
348 case DEVICE_REMOVED:
349 case PORT_ADDED:
350 case PORT_UPDATED:
351 case PORT_REMOVED:
352 default:
353 break;
354 }
355 }
356 }
357
alshabib030111e2014-09-15 15:56:42 -0700358}
359
tomc370ebd2014-09-16 01:25:21 -0700360