blob: fcf8efbdc38eec24a06ed693b9b9412a390284cf [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;
Brian O'Connorabafb502014-12-02 22:26:20 -080030import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.net.Host;
33import org.onosproject.net.HostId;
34import org.onosproject.net.Path;
35import org.onosproject.net.PortNumber;
36import org.onosproject.net.flow.DefaultFlowRule;
37import org.onosproject.net.flow.DefaultTrafficSelector;
38import org.onosproject.net.flow.DefaultTrafficTreatment;
39import org.onosproject.net.flow.FlowRule;
40import org.onosproject.net.flow.FlowRuleService;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.onosproject.net.host.HostService;
44import org.onosproject.net.packet.InboundPacket;
45import org.onosproject.net.packet.PacketContext;
46import org.onosproject.net.packet.PacketProcessor;
47import org.onosproject.net.packet.PacketService;
48import org.onosproject.net.topology.TopologyService;
alshabib7b2748f2014-09-16 20:21:11 -070049import org.onlab.packet.Ethernet;
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
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900170 // Bail if this is deemed to be a control packet.
171 if (isControlPacket(ethPkt)) {
172 return;
173 }
174
175 // Skip IPv6 multicast packet when IPv6 forward is disabled.
176 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800177 return;
178 }
179
tom642b2262014-09-17 13:52:55 -0700180 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700181
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700182 // Do not process link-local addresses in any way.
183 if (id.mac().isLinkLocal()) {
184 return;
185 }
186
tomc370ebd2014-09-16 01:25:21 -0700187 // Do we know who this is for? If not, flood and bail.
188 Host dst = hostService.getHost(id);
189 if (dst == null) {
190 flood(context);
191 return;
192 }
193
194 // Are we on an edge switch that our destination is on? If so,
195 // simply forward out to the destination and bail.
196 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700197 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
198 installRule(context, dst.location().port());
199 }
tomc370ebd2014-09-16 01:25:21 -0700200 return;
201 }
202
203 // Otherwise, get a set of paths that lead from here to the
204 // destination edge switch.
205 Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700206 pkt.receivedFrom().deviceId(),
207 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700208 if (paths.isEmpty()) {
209 // If there are no paths, flood and bail.
210 flood(context);
211 return;
212 }
213
214 // Otherwise, pick a path that does not lead back to where we
215 // came from; if no such path, flood and bail.
216 Path path = pickForwardPath(paths, pkt.receivedFrom().port());
217 if (path == null) {
tom642b2262014-09-17 13:52:55 -0700218 log.warn("Doh... don't know where to go... {} -> {} received on {}",
tomc16656f2014-10-15 18:30:31 -0700219 ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
220 pkt.receivedFrom());
tomc370ebd2014-09-16 01:25:21 -0700221 flood(context);
222 return;
223 }
224
225 // Otherwise forward and be done with it.
alshabib7b2748f2014-09-16 20:21:11 -0700226 installRule(context, path.src().port());
tomc370ebd2014-09-16 01:25:21 -0700227 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800228
229 }
230
231 // Indicates whether this is a control packet, e.g. LLDP, BDDP
232 private boolean isControlPacket(Ethernet eth) {
233 short type = eth.getEtherType();
234 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700235 }
236
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800237 // Indicated whether this is an IPv6 multicast packet.
238 private boolean isIpv6Multicast(Ethernet eth) {
239 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
240 }
241
tomc370ebd2014-09-16 01:25:21 -0700242 // Selects a path from the given set that does not lead back to the
243 // specified port.
244 private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) {
245 for (Path path : paths) {
246 if (!path.src().port().equals(notToPort)) {
247 return path;
248 }
249 }
250 return null;
251 }
252
tom642b2262014-09-17 13:52:55 -0700253 // Floods the specified packet if permissible.
tomc370ebd2014-09-16 01:25:21 -0700254 private void flood(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700255 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700256 context.inPacket().receivedFrom())) {
tom642b2262014-09-17 13:52:55 -0700257 packetOut(context, PortNumber.FLOOD);
tomc370ebd2014-09-16 01:25:21 -0700258 } else {
259 context.block();
260 }
261 }
262
tom642b2262014-09-17 13:52:55 -0700263 // Sends a packet out the specified port.
264 private void packetOut(PacketContext context, PortNumber portNumber) {
alshabib010c31d2014-09-26 10:01:12 -0700265 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700266 context.send();
267 }
268
269 // Install a rule forwarding the packet to the specified port.
270 private void installRule(PacketContext context, PortNumber portNumber) {
tom642b2262014-09-17 13:52:55 -0700271 // We don't yet support bufferids in the flowservice so packet out first.
272 packetOut(context, portNumber);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800273 if (!packetOutOnly) {
274 // Install the flow rule to handle this type of message from now on.
275 Ethernet inPkt = context.inPacket().parsed();
276 TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
277 builder.matchEthType(inPkt.getEtherType())
278 .matchEthSrc(inPkt.getSourceMAC())
279 .matchEthDst(inPkt.getDestinationMAC())
280 .matchInport(context.inPacket().receivedFrom().port());
alshabib7b2748f2014-09-16 20:21:11 -0700281
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800282 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
283 treat.setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700284
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800285 FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
286 builder.build(), treat.build(), PRIORITY, appId, TIMEOUT, false);
alshabib6eb438a2014-10-01 16:39:37 -0700287
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800288 flowRuleService.applyFlowRules(f);
289 }
tomc370ebd2014-09-16 01:25:21 -0700290 }
291
alshabib030111e2014-09-15 15:56:42 -0700292}
293
tomc370ebd2014-09-16 01:25:21 -0700294