blob: 06febb14851d7016ae138d28b4e31745b3356064 [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;
47import org.onosproject.net.packet.PacketProcessor;
48import org.onosproject.net.packet.PacketService;
49import org.onosproject.net.topology.TopologyService;
tomc16656f2014-10-15 18:30:31 -070050import org.osgi.service.component.ComponentContext;
tomc370ebd2014-09-16 01:25:21 -070051import org.slf4j.Logger;
alshabib030111e2014-09-15 15:56:42 -070052
tomc370ebd2014-09-16 01:25:21 -070053/**
54 * Sample reactive forwarding application.
55 */
56@Component(immediate = true)
alshabib030111e2014-09-15 15:56:42 -070057public class ReactiveForwarding {
58
alshabibba5ac482014-10-02 17:15:20 -070059 private static final int TIMEOUT = 10;
alshabiba0e04982014-10-03 13:03:19 -070060 private static final int PRIORITY = 10;
alshabibba5ac482014-10-02 17:15:20 -070061
tomc370ebd2014-09-16 01:25:21 -070062 private final Logger log = getLogger(getClass());
63
alshabib030111e2014-09-15 15:56:42 -070064 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
65 protected TopologyService topologyService;
66
67 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 protected PacketService packetService;
69
alshabib8aef1ad2014-09-15 17:47:31 -070070 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
71 protected HostService hostService;
72
alshabib7b2748f2014-09-16 20:21:11 -070073 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected FlowRuleService flowRuleService;
75
alshabib92c65ad2014-10-08 21:56:05 -070076 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected CoreService coreService;
78
tomc370ebd2014-09-16 01:25:21 -070079 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -070080
alshabiba68eb962014-09-24 20:34:13 -070081 private ApplicationId appId;
82
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -080083 @Property(name = "packetOutOnly", boolValue = false,
84 label = "Enable packet-out only forwarding; default is false")
85 private boolean packetOutOnly = false;
tomc16656f2014-10-15 18:30:31 -070086
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +090087 @Property(name = "ipv6Forwarding", boolValue = false,
88 label = "Enable IPv6 forwarding; default is false")
89 private boolean ipv6Forwarding = false;
90
alshabib030111e2014-09-15 15:56:42 -070091 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +090092 public void activate(ComponentContext context) {
Brian O'Connorabafb502014-12-02 22:26:20 -080093 appId = coreService.registerApplication("org.onosproject.fwd");
alshabibc274c902014-10-03 14:58:27 -070094 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +090095 readComponentConfiguration(context);
alshabiba68eb962014-09-24 20:34:13 -070096 log.info("Started with Application ID {}", appId.id());
alshabib030111e2014-09-15 15:56:42 -070097 }
98
99 @Deactivate
100 public void deactivate() {
alshabiba68eb962014-09-24 20:34:13 -0700101 flowRuleService.removeFlowRulesById(appId);
alshabib030111e2014-09-15 15:56:42 -0700102 packetService.removeProcessor(processor);
103 processor = null;
tomc370ebd2014-09-16 01:25:21 -0700104 log.info("Stopped");
alshabib030111e2014-09-15 15:56:42 -0700105 }
tomc370ebd2014-09-16 01:25:21 -0700106
tomc16656f2014-10-15 18:30:31 -0700107 @Modified
108 public void modified(ComponentContext context) {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900109 readComponentConfiguration(context);
110 }
111
112 /**
113 * Extracts properties from the component configuration context.
114 *
115 * @param context the component context
116 */
117 private void readComponentConfiguration(ComponentContext context) {
118 Dictionary<?, ?> properties = context.getProperties();
119 boolean packetOutOnlyEnabled = isPropertyEnabled(properties, "packetOutOnly");
120 if (packetOutOnly != packetOutOnlyEnabled) {
121 packetOutOnly = packetOutOnlyEnabled;
122 log.info("Configured. Packet-out only forwarding is {}",
123 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700124 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900125 boolean ipv6ForwardingEnabled = isPropertyEnabled(properties, "ipv6Forwarding");
126 if (ipv6Forwarding != ipv6ForwardingEnabled) {
127 ipv6Forwarding = ipv6ForwardingEnabled;
128 log.info("Configured. IPv6 forwarding is {}",
129 ipv6Forwarding ? "enabled" : "disabled");
130 }
131 }
132
133 /**
134 * Check property name is defined and set to true.
135 *
136 * @param properties properties to be looked up
137 * @param propertyName the name of the property to look up
138 * @return true when the propertyName is defined and set to true
139 */
140 private static boolean isPropertyEnabled(Dictionary<?, ?> properties, String propertyName) {
141 boolean enabled = false;
142 try {
143 String flag = (String) properties.get(propertyName);
144 if (flag != null) {
145 enabled = flag.equals("true");
146 }
147 } catch (ClassCastException e) {
148 // No propertyName defined.
149 enabled = false;
150 }
151 return enabled;
tomc16656f2014-10-15 18:30:31 -0700152 }
tomc370ebd2014-09-16 01:25:21 -0700153
154 /**
155 * Packet processor responsible for forwarding packets along their paths.
156 */
157 private class ReactivePacketProcessor implements PacketProcessor {
158
159 @Override
160 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700161 // Stop processing if the packet has been handled, since we
162 // can't do any more to it.
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800163 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700164 return;
165 }
tomdc95b8a2014-09-17 08:07:26 -0700166
tomc370ebd2014-09-16 01:25:21 -0700167 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700168 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700169
Jonathan Harte8600eb2015-01-12 10:30:45 -0800170 if (ethPkt == null) {
171 return;
172 }
173
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900174 // Bail if this is deemed to be a control packet.
175 if (isControlPacket(ethPkt)) {
176 return;
177 }
178
179 // Skip IPv6 multicast packet when IPv6 forward is disabled.
180 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800181 return;
182 }
183
tom642b2262014-09-17 13:52:55 -0700184 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700185
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700186 // Do not process link-local addresses in any way.
187 if (id.mac().isLinkLocal()) {
188 return;
189 }
190
tomc370ebd2014-09-16 01:25:21 -0700191 // Do we know who this is for? If not, flood and bail.
192 Host dst = hostService.getHost(id);
193 if (dst == null) {
194 flood(context);
195 return;
196 }
197
198 // Are we on an edge switch that our destination is on? If so,
199 // simply forward out to the destination and bail.
200 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700201 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
202 installRule(context, dst.location().port());
203 }
tomc370ebd2014-09-16 01:25:21 -0700204 return;
205 }
206
207 // Otherwise, get a set of paths that lead from here to the
208 // destination edge switch.
209 Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700210 pkt.receivedFrom().deviceId(),
211 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700212 if (paths.isEmpty()) {
213 // If there are no paths, flood and bail.
214 flood(context);
215 return;
216 }
217
218 // Otherwise, pick a path that does not lead back to where we
219 // came from; if no such path, flood and bail.
220 Path path = pickForwardPath(paths, pkt.receivedFrom().port());
221 if (path == null) {
tom642b2262014-09-17 13:52:55 -0700222 log.warn("Doh... don't know where to go... {} -> {} received on {}",
tomc16656f2014-10-15 18:30:31 -0700223 ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
224 pkt.receivedFrom());
tomc370ebd2014-09-16 01:25:21 -0700225 flood(context);
226 return;
227 }
228
229 // Otherwise forward and be done with it.
alshabib7b2748f2014-09-16 20:21:11 -0700230 installRule(context, path.src().port());
tomc370ebd2014-09-16 01:25:21 -0700231 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800232
233 }
234
235 // Indicates whether this is a control packet, e.g. LLDP, BDDP
236 private boolean isControlPacket(Ethernet eth) {
237 short type = eth.getEtherType();
238 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700239 }
240
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800241 // Indicated whether this is an IPv6 multicast packet.
242 private boolean isIpv6Multicast(Ethernet eth) {
243 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
244 }
245
tomc370ebd2014-09-16 01:25:21 -0700246 // Selects a path from the given set that does not lead back to the
247 // specified port.
248 private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) {
249 for (Path path : paths) {
250 if (!path.src().port().equals(notToPort)) {
251 return path;
252 }
253 }
254 return null;
255 }
256
tom642b2262014-09-17 13:52:55 -0700257 // Floods the specified packet if permissible.
tomc370ebd2014-09-16 01:25:21 -0700258 private void flood(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700259 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700260 context.inPacket().receivedFrom())) {
tom642b2262014-09-17 13:52:55 -0700261 packetOut(context, PortNumber.FLOOD);
tomc370ebd2014-09-16 01:25:21 -0700262 } else {
263 context.block();
264 }
265 }
266
tom642b2262014-09-17 13:52:55 -0700267 // Sends a packet out the specified port.
268 private void packetOut(PacketContext context, PortNumber portNumber) {
alshabib010c31d2014-09-26 10:01:12 -0700269 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700270 context.send();
271 }
272
273 // Install a rule forwarding the packet to the specified port.
274 private void installRule(PacketContext context, PortNumber portNumber) {
tom642b2262014-09-17 13:52:55 -0700275 // We don't yet support bufferids in the flowservice so packet out first.
276 packetOut(context, portNumber);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800277 if (!packetOutOnly) {
278 // Install the flow rule to handle this type of message from now on.
279 Ethernet inPkt = context.inPacket().parsed();
280 TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
281 builder.matchEthType(inPkt.getEtherType())
282 .matchEthSrc(inPkt.getSourceMAC())
283 .matchEthDst(inPkt.getDestinationMAC())
284 .matchInport(context.inPacket().receivedFrom().port());
alshabib7b2748f2014-09-16 20:21:11 -0700285
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800286 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
287 treat.setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700288
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800289 FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
290 builder.build(), treat.build(), PRIORITY, appId, TIMEOUT, false);
alshabib6eb438a2014-10-01 16:39:37 -0700291
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800292 flowRuleService.applyFlowRules(f);
293 }
tomc370ebd2014-09-16 01:25:21 -0700294 }
295
alshabib030111e2014-09-15 15:56:42 -0700296}
297
tomc370ebd2014-09-16 01:25:21 -0700298