blob: dc925e6ba49daef2b3044a325fc74fae4b519dd3 [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;
33import org.onosproject.net.Host;
34import org.onosproject.net.HostId;
35import org.onosproject.net.Path;
36import org.onosproject.net.PortNumber;
37import org.onosproject.net.flow.DefaultFlowRule;
38import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.FlowRule;
41import org.onosproject.net.flow.FlowRuleService;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.host.HostService;
45import org.onosproject.net.packet.InboundPacket;
46import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080047import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.packet.PacketProcessor;
49import org.onosproject.net.packet.PacketService;
50import org.onosproject.net.topology.TopologyService;
tomc16656f2014-10-15 18:30:31 -070051import org.osgi.service.component.ComponentContext;
tomc370ebd2014-09-16 01:25:21 -070052import org.slf4j.Logger;
alshabib030111e2014-09-15 15:56:42 -070053
tomc370ebd2014-09-16 01:25:21 -070054/**
55 * Sample reactive forwarding application.
56 */
57@Component(immediate = true)
alshabib030111e2014-09-15 15:56:42 -070058public class ReactiveForwarding {
59
alshabibba5ac482014-10-02 17:15:20 -070060 private static final int TIMEOUT = 10;
alshabiba0e04982014-10-03 13:03:19 -070061 private static final int PRIORITY = 10;
alshabibba5ac482014-10-02 17:15:20 -070062
tomc370ebd2014-09-16 01:25:21 -070063 private final Logger log = getLogger(getClass());
64
alshabib030111e2014-09-15 15:56:42 -070065 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected TopologyService topologyService;
67
68 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected PacketService packetService;
70
alshabib8aef1ad2014-09-15 17:47:31 -070071 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected HostService hostService;
73
alshabib7b2748f2014-09-16 20:21:11 -070074 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected FlowRuleService flowRuleService;
76
alshabib92c65ad2014-10-08 21:56:05 -070077 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected CoreService coreService;
79
tomc370ebd2014-09-16 01:25:21 -070080 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -070081
alshabiba68eb962014-09-24 20:34:13 -070082 private ApplicationId appId;
83
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -080084 @Property(name = "packetOutOnly", boolValue = false,
85 label = "Enable packet-out only forwarding; default is false")
86 private boolean packetOutOnly = false;
tomc16656f2014-10-15 18:30:31 -070087
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +090088 @Property(name = "ipv6Forwarding", boolValue = false,
89 label = "Enable IPv6 forwarding; default is false")
90 private boolean ipv6Forwarding = false;
91
alshabib030111e2014-09-15 15:56:42 -070092 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +090093 public void activate(ComponentContext context) {
Brian O'Connorabafb502014-12-02 22:26:20 -080094 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080095
alshabibc274c902014-10-03 14:58:27 -070096 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +090097 readComponentConfiguration(context);
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080098
99 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
100 selector.matchEthType(Ethernet.TYPE_IPV4);
101 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
102
alshabiba68eb962014-09-24 20:34:13 -0700103 log.info("Started with Application ID {}", appId.id());
alshabib030111e2014-09-15 15:56:42 -0700104 }
105
106 @Deactivate
107 public void deactivate() {
alshabiba68eb962014-09-24 20:34:13 -0700108 flowRuleService.removeFlowRulesById(appId);
alshabib030111e2014-09-15 15:56:42 -0700109 packetService.removeProcessor(processor);
110 processor = null;
tomc370ebd2014-09-16 01:25:21 -0700111 log.info("Stopped");
alshabib030111e2014-09-15 15:56:42 -0700112 }
tomc370ebd2014-09-16 01:25:21 -0700113
tomc16656f2014-10-15 18:30:31 -0700114 @Modified
115 public void modified(ComponentContext context) {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900116 readComponentConfiguration(context);
117 }
118
119 /**
120 * Extracts properties from the component configuration context.
121 *
122 * @param context the component context
123 */
124 private void readComponentConfiguration(ComponentContext context) {
125 Dictionary<?, ?> properties = context.getProperties();
126 boolean packetOutOnlyEnabled = isPropertyEnabled(properties, "packetOutOnly");
127 if (packetOutOnly != packetOutOnlyEnabled) {
128 packetOutOnly = packetOutOnlyEnabled;
129 log.info("Configured. Packet-out only forwarding is {}",
130 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700131 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900132 boolean ipv6ForwardingEnabled = isPropertyEnabled(properties, "ipv6Forwarding");
133 if (ipv6Forwarding != ipv6ForwardingEnabled) {
134 ipv6Forwarding = ipv6ForwardingEnabled;
135 log.info("Configured. IPv6 forwarding is {}",
136 ipv6Forwarding ? "enabled" : "disabled");
137 }
138 }
139
140 /**
141 * Check property name is defined and set to true.
142 *
143 * @param properties properties to be looked up
144 * @param propertyName the name of the property to look up
145 * @return true when the propertyName is defined and set to true
146 */
147 private static boolean isPropertyEnabled(Dictionary<?, ?> properties, String propertyName) {
148 boolean enabled = false;
149 try {
150 String flag = (String) properties.get(propertyName);
151 if (flag != null) {
152 enabled = flag.equals("true");
153 }
154 } catch (ClassCastException e) {
155 // No propertyName defined.
156 enabled = false;
157 }
158 return enabled;
tomc16656f2014-10-15 18:30:31 -0700159 }
tomc370ebd2014-09-16 01:25:21 -0700160
161 /**
162 * Packet processor responsible for forwarding packets along their paths.
163 */
164 private class ReactivePacketProcessor implements PacketProcessor {
165
166 @Override
167 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700168 // Stop processing if the packet has been handled, since we
169 // can't do any more to it.
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800170 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700171 return;
172 }
tomdc95b8a2014-09-17 08:07:26 -0700173
tomc370ebd2014-09-16 01:25:21 -0700174 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700175 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700176
Jonathan Harte8600eb2015-01-12 10:30:45 -0800177 if (ethPkt == null) {
178 return;
179 }
180
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900181 // Bail if this is deemed to be a control packet.
182 if (isControlPacket(ethPkt)) {
183 return;
184 }
185
186 // Skip IPv6 multicast packet when IPv6 forward is disabled.
187 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800188 return;
189 }
190
tom642b2262014-09-17 13:52:55 -0700191 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700192
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700193 // Do not process link-local addresses in any way.
194 if (id.mac().isLinkLocal()) {
195 return;
196 }
197
tomc370ebd2014-09-16 01:25:21 -0700198 // Do we know who this is for? If not, flood and bail.
199 Host dst = hostService.getHost(id);
200 if (dst == null) {
201 flood(context);
202 return;
203 }
204
205 // Are we on an edge switch that our destination is on? If so,
206 // simply forward out to the destination and bail.
207 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700208 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
209 installRule(context, dst.location().port());
210 }
tomc370ebd2014-09-16 01:25:21 -0700211 return;
212 }
213
214 // Otherwise, get a set of paths that lead from here to the
215 // destination edge switch.
216 Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700217 pkt.receivedFrom().deviceId(),
218 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700219 if (paths.isEmpty()) {
220 // If there are no paths, flood and bail.
221 flood(context);
222 return;
223 }
224
225 // Otherwise, pick a path that does not lead back to where we
226 // came from; if no such path, flood and bail.
227 Path path = pickForwardPath(paths, pkt.receivedFrom().port());
228 if (path == null) {
tom642b2262014-09-17 13:52:55 -0700229 log.warn("Doh... don't know where to go... {} -> {} received on {}",
tomc16656f2014-10-15 18:30:31 -0700230 ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
231 pkt.receivedFrom());
tomc370ebd2014-09-16 01:25:21 -0700232 flood(context);
233 return;
234 }
235
236 // Otherwise forward and be done with it.
alshabib7b2748f2014-09-16 20:21:11 -0700237 installRule(context, path.src().port());
tomc370ebd2014-09-16 01:25:21 -0700238 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800239
240 }
241
242 // Indicates whether this is a control packet, e.g. LLDP, BDDP
243 private boolean isControlPacket(Ethernet eth) {
244 short type = eth.getEtherType();
245 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700246 }
247
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800248 // Indicated whether this is an IPv6 multicast packet.
249 private boolean isIpv6Multicast(Ethernet eth) {
250 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
251 }
252
tomc370ebd2014-09-16 01:25:21 -0700253 // Selects a path from the given set that does not lead back to the
254 // specified port.
255 private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) {
256 for (Path path : paths) {
257 if (!path.src().port().equals(notToPort)) {
258 return path;
259 }
260 }
261 return null;
262 }
263
tom642b2262014-09-17 13:52:55 -0700264 // Floods the specified packet if permissible.
tomc370ebd2014-09-16 01:25:21 -0700265 private void flood(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700266 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700267 context.inPacket().receivedFrom())) {
tom642b2262014-09-17 13:52:55 -0700268 packetOut(context, PortNumber.FLOOD);
tomc370ebd2014-09-16 01:25:21 -0700269 } else {
270 context.block();
271 }
272 }
273
tom642b2262014-09-17 13:52:55 -0700274 // Sends a packet out the specified port.
275 private void packetOut(PacketContext context, PortNumber portNumber) {
alshabib010c31d2014-09-26 10:01:12 -0700276 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700277 context.send();
278 }
279
280 // Install a rule forwarding the packet to the specified port.
281 private void installRule(PacketContext context, PortNumber portNumber) {
tom642b2262014-09-17 13:52:55 -0700282 // We don't yet support bufferids in the flowservice so packet out first.
283 packetOut(context, portNumber);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800284 if (!packetOutOnly) {
285 // Install the flow rule to handle this type of message from now on.
286 Ethernet inPkt = context.inPacket().parsed();
287 TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
288 builder.matchEthType(inPkt.getEtherType())
289 .matchEthSrc(inPkt.getSourceMAC())
290 .matchEthDst(inPkt.getDestinationMAC())
291 .matchInport(context.inPacket().receivedFrom().port());
alshabib7b2748f2014-09-16 20:21:11 -0700292
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800293 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
294 treat.setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700295
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800296 FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
297 builder.build(), treat.build(), PRIORITY, appId, TIMEOUT, false);
alshabib6eb438a2014-10-01 16:39:37 -0700298
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800299 flowRuleService.applyFlowRules(f);
300 }
tomc370ebd2014-09-16 01:25:21 -0700301 }
302
alshabib030111e2014-09-15 15:56:42 -0700303}
304
tomc370ebd2014-09-16 01:25:21 -0700305