blob: c557dbbfb4691c48561f9f0ca3a1b360b341ad2c [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 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
18import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
tomc16656f2014-10-15 18:30:31 -070021import org.apache.felix.scr.annotations.Modified;
22import org.apache.felix.scr.annotations.Property;
alshabib030111e2014-09-15 15:56:42 -070023import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harte8600eb2015-01-12 10:30:45 -080025import org.onlab.packet.Ethernet;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010026import org.onlab.packet.ICMP;
27import org.onlab.packet.ICMP6;
Jonathan Hart430223a2015-04-22 17:39:02 -070028import org.onlab.packet.IPv4;
29import org.onlab.packet.IPv6;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010030import org.onlab.packet.Ip4Prefix;
31import org.onlab.packet.Ip6Prefix;
Jonathan Hart430223a2015-04-22 17:39:02 -070032import org.onlab.packet.TCP;
33import org.onlab.packet.UDP;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010034import org.onlab.packet.VlanId;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070035import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080036import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
38import org.onosproject.net.Host;
39import org.onosproject.net.HostId;
40import org.onosproject.net.Path;
41import org.onosproject.net.PortNumber;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.flow.FlowRuleService;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
Jonathan Hart3b881aa2015-04-22 18:03:50 -070047import org.onosproject.net.flowobjective.DefaultForwardingObjective;
48import org.onosproject.net.flowobjective.FlowObjectiveService;
49import org.onosproject.net.flowobjective.ForwardingObjective;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.host.HostService;
51import org.onosproject.net.packet.InboundPacket;
52import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080053import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080054import org.onosproject.net.packet.PacketProcessor;
55import org.onosproject.net.packet.PacketService;
56import org.onosproject.net.topology.TopologyService;
tomc16656f2014-10-15 18:30:31 -070057import org.osgi.service.component.ComponentContext;
tomc370ebd2014-09-16 01:25:21 -070058import org.slf4j.Logger;
alshabib030111e2014-09-15 15:56:42 -070059
Jonathan Hart430223a2015-04-22 17:39:02 -070060import java.util.Dictionary;
61import java.util.Set;
62
63import static com.google.common.base.Strings.isNullOrEmpty;
64import static org.slf4j.LoggerFactory.getLogger;
65
tomc370ebd2014-09-16 01:25:21 -070066/**
67 * Sample reactive forwarding application.
68 */
69@Component(immediate = true)
alshabib030111e2014-09-15 15:56:42 -070070public class ReactiveForwarding {
71
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010072 private static final int DEFAULT_TIMEOUT = 10;
73 private static final int DEFAULT_PRIORITY = 10;
alshabibba5ac482014-10-02 17:15:20 -070074
tomc370ebd2014-09-16 01:25:21 -070075 private final Logger log = getLogger(getClass());
76
alshabib030111e2014-09-15 15:56:42 -070077 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
78 protected TopologyService topologyService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
81 protected PacketService packetService;
82
alshabib8aef1ad2014-09-15 17:47:31 -070083 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
84 protected HostService hostService;
85
alshabib7b2748f2014-09-16 20:21:11 -070086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected FlowRuleService flowRuleService;
88
alshabib92c65ad2014-10-08 21:56:05 -070089 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart3b881aa2015-04-22 18:03:50 -070090 protected FlowObjectiveService flowObjectiveService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib92c65ad2014-10-08 21:56:05 -070093 protected CoreService coreService;
94
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070095 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
96 protected ComponentConfigService cfgService;
97
tomc370ebd2014-09-16 01:25:21 -070098 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -070099
alshabiba68eb962014-09-24 20:34:13 -0700100 private ApplicationId appId;
101
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800102 @Property(name = "packetOutOnly", boolValue = false,
103 label = "Enable packet-out only forwarding; default is false")
104 private boolean packetOutOnly = false;
tomc16656f2014-10-15 18:30:31 -0700105
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100106 @Property(name = "packetOutOfppTable", boolValue = false,
107 label = "Enable first packet forwarding using OFPP_TABLE port " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700108 "instead of PacketOut with actual port; default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100109 private boolean packetOutOfppTable = false;
110
111 @Property(name = "flowTimeout", intValue = DEFAULT_TIMEOUT,
112 label = "Configure Flow Timeout for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700113 "default is 10 sec")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100114 private int flowTimeout = DEFAULT_TIMEOUT;
115
116 @Property(name = "flowPriority", intValue = DEFAULT_PRIORITY,
117 label = "Configure Flow Priority for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700118 "default is 10")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100119 private int flowPriority = DEFAULT_PRIORITY;
120
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900121 @Property(name = "ipv6Forwarding", boolValue = false,
122 label = "Enable IPv6 forwarding; default is false")
123 private boolean ipv6Forwarding = false;
124
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100125 @Property(name = "matchDstMacOnly", boolValue = false,
126 label = "Enable matching Dst Mac Only; default is false")
127 private boolean matchDstMacOnly = false;
128
129 @Property(name = "matchVlanId", boolValue = false,
130 label = "Enable matching Vlan ID; default is false")
131 private boolean matchVlanId = false;
132
133 @Property(name = "matchIpv4Address", boolValue = false,
134 label = "Enable matching IPv4 Addresses; default is false")
135 private boolean matchIpv4Address = false;
136
137 @Property(name = "matchIpv4Dscp", boolValue = false,
138 label = "Enable matching IPv4 DSCP and ECN; default is false")
139 private boolean matchIpv4Dscp = false;
140
141 @Property(name = "matchIpv6Address", boolValue = false,
142 label = "Enable matching IPv6 Addresses; default is false")
143 private boolean matchIpv6Address = false;
144
145 @Property(name = "matchIpv6FlowLabel", boolValue = false,
146 label = "Enable matching IPv6 FlowLabel; default is false")
147 private boolean matchIpv6FlowLabel = false;
148
149 @Property(name = "matchTcpUdpPorts", boolValue = false,
150 label = "Enable matching TCP/UDP ports; default is false")
151 private boolean matchTcpUdpPorts = false;
152
153 @Property(name = "matchIcmpFields", boolValue = false,
154 label = "Enable matching ICMPv4 and ICMPv6 fields; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700155 "default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100156 private boolean matchIcmpFields = false;
157
Rusty Eddy29acad62015-07-07 19:33:47 -0700158 @Property(name = "ignoreIPv4Multicast", boolValue = false,
159 label = "Ignore (do not forward) IPv4 multicast packets; default is false")
160 private boolean ignoreIpv4McastPackets = false;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100161
alshabib030111e2014-09-15 15:56:42 -0700162 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900163 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700164 cfgService.registerProperties(getClass());
Brian O'Connorabafb502014-12-02 22:26:20 -0800165 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800166
alshabibc274c902014-10-03 14:58:27 -0700167 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900168 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700169 requestIntercepts();
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800170
Charles M.C. Chane148de82015-05-06 12:38:21 +0800171 log.info("Started with Application ID {}", appId.id());
172 }
173
174 @Deactivate
175 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800176 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700177 withdrawIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800178 flowRuleService.removeFlowRulesById(appId);
179 packetService.removeProcessor(processor);
180 processor = null;
181 log.info("Stopped");
182 }
183
184 @Modified
185 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800186 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700187 requestIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800188 }
189
190 /**
191 * Request packet in via PacketService.
192 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700193 private void requestIntercepts() {
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800194 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
195 selector.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700196 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100197 selector.matchEthType(Ethernet.TYPE_ARP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700198 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100199
Thomas Vachuska27bee092015-06-23 19:03:10 -0700200 selector.matchEthType(Ethernet.TYPE_IPV6);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100201 if (ipv6Forwarding) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700202 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
203 } else {
204 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100205 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900206 }
207
208 /**
Aaron Kruglikov07a923d2015-07-03 13:30:57 -0700209 * Cancel request for packet in via PacketService.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700210 */
211 private void withdrawIntercepts() {
212 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
213 selector.matchEthType(Ethernet.TYPE_IPV4);
214 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
215 selector.matchEthType(Ethernet.TYPE_ARP);
216 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
217 selector.matchEthType(Ethernet.TYPE_IPV6);
218 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
219 }
220
221 /**
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900222 * Extracts properties from the component configuration context.
223 *
224 * @param context the component context
225 */
226 private void readComponentConfiguration(ComponentContext context) {
227 Dictionary<?, ?> properties = context.getProperties();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100228 boolean packetOutOnlyEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700229 isPropertyEnabled(properties, "packetOutOnly");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900230 if (packetOutOnly != packetOutOnlyEnabled) {
231 packetOutOnly = packetOutOnlyEnabled;
232 log.info("Configured. Packet-out only forwarding is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700233 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700234 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100235 boolean packetOutOfppTableEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700236 isPropertyEnabled(properties, "packetOutOfppTable");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100237 if (packetOutOfppTable != packetOutOfppTableEnabled) {
238 packetOutOfppTable = packetOutOfppTableEnabled;
239 log.info("Configured. Forwarding using OFPP_TABLE port is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700240 packetOutOfppTable ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100241 }
242 boolean ipv6ForwardingEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700243 isPropertyEnabled(properties, "ipv6Forwarding");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900244 if (ipv6Forwarding != ipv6ForwardingEnabled) {
245 ipv6Forwarding = ipv6ForwardingEnabled;
246 log.info("Configured. IPv6 forwarding is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700247 ipv6Forwarding ? "enabled" : "disabled");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900248 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100249 boolean matchDstMacOnlyEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700250 isPropertyEnabled(properties, "matchDstMacOnly");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100251 if (matchDstMacOnly != matchDstMacOnlyEnabled) {
252 matchDstMacOnly = matchDstMacOnlyEnabled;
253 log.info("Configured. Match Dst MAC Only is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700254 matchDstMacOnly ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100255 }
256 boolean matchVlanIdEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700257 isPropertyEnabled(properties, "matchVlanId");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100258 if (matchVlanId != matchVlanIdEnabled) {
259 matchVlanId = matchVlanIdEnabled;
260 log.info("Configured. Matching Vlan ID is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700261 matchVlanId ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100262 }
263 boolean matchIpv4AddressEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700264 isPropertyEnabled(properties, "matchIpv4Address");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100265 if (matchIpv4Address != matchIpv4AddressEnabled) {
266 matchIpv4Address = matchIpv4AddressEnabled;
267 log.info("Configured. Matching IPv4 Addresses is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700268 matchIpv4Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100269 }
270 boolean matchIpv4DscpEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700271 isPropertyEnabled(properties, "matchIpv4Dscp");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100272 if (matchIpv4Dscp != matchIpv4DscpEnabled) {
273 matchIpv4Dscp = matchIpv4DscpEnabled;
274 log.info("Configured. Matching IPv4 DSCP and ECN is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700275 matchIpv4Dscp ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100276 }
277 boolean matchIpv6AddressEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700278 isPropertyEnabled(properties, "matchIpv6Address");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100279 if (matchIpv6Address != matchIpv6AddressEnabled) {
280 matchIpv6Address = matchIpv6AddressEnabled;
281 log.info("Configured. Matching IPv6 Addresses is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700282 matchIpv6Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100283 }
284 boolean matchIpv6FlowLabelEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700285 isPropertyEnabled(properties, "matchIpv6FlowLabel");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100286 if (matchIpv6FlowLabel != matchIpv6FlowLabelEnabled) {
287 matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
288 log.info("Configured. Matching IPv6 FlowLabel is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700289 matchIpv6FlowLabel ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100290 }
291 boolean matchTcpUdpPortsEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700292 isPropertyEnabled(properties, "matchTcpUdpPorts");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100293 if (matchTcpUdpPorts != matchTcpUdpPortsEnabled) {
294 matchTcpUdpPorts = matchTcpUdpPortsEnabled;
295 log.info("Configured. Matching TCP/UDP fields is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700296 matchTcpUdpPorts ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100297 }
298 boolean matchIcmpFieldsEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700299 isPropertyEnabled(properties, "matchIcmpFields");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100300 if (matchIcmpFields != matchIcmpFieldsEnabled) {
301 matchIcmpFields = matchIcmpFieldsEnabled;
302 log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700303 matchIcmpFields ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100304 }
305 Integer flowTimeoutConfigured =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700306 getIntegerProperty(properties, "flowTimeout");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100307 if (flowTimeoutConfigured == null) {
308 log.info("Flow Timeout is not configured, default value is {}",
309 flowTimeout);
310 } else {
311 flowTimeout = flowTimeoutConfigured;
312 log.info("Configured. Flow Timeout is configured to {}",
313 flowTimeout, " seconds");
314 }
315 Integer flowPriorityConfigured =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700316 getIntegerProperty(properties, "flowPriority");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100317 if (flowPriorityConfigured == null) {
318 log.info("Flow Priority is not configured, default value is {}",
319 flowPriority);
320 } else {
321 flowPriority = flowPriorityConfigured;
322 log.info("Configured. Flow Priority is configured to {}",
323 flowPriority);
324 }
Rusty Eddy29acad62015-07-07 19:33:47 -0700325
326 boolean ignoreIpv4McastPacketsEnabled =
327 isPropertyEnabled(properties, "ignoreIpv4McastPackets");
328 if (ignoreIpv4McastPackets != ignoreIpv4McastPacketsEnabled) {
329 ignoreIpv4McastPackets = ignoreIpv4McastPacketsEnabled;
330 log.info("Configured. Ignore IPv4 multicast packets is {}",
331 ignoreIpv4McastPackets ? "enabled" : "disabled");
332 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100333 }
334
335 /**
336 * Get Integer property from the propertyName
337 * Return null if propertyName is not found.
338 *
Thomas Vachuska27bee092015-06-23 19:03:10 -0700339 * @param properties properties to be looked up
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100340 * @param propertyName the name of the property to look up
341 * @return value when the propertyName is defined or return null
342 */
343 private static Integer getIntegerProperty(Dictionary<?, ?> properties,
344 String propertyName) {
345 Integer value = null;
346 try {
347 String s = (String) properties.get(propertyName);
348 value = isNullOrEmpty(s) ? value : Integer.parseInt(s.trim());
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800349 } catch (NumberFormatException | ClassCastException e) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100350 value = null;
351 }
352 return value;
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900353 }
354
355 /**
356 * Check property name is defined and set to true.
357 *
Thomas Vachuska27bee092015-06-23 19:03:10 -0700358 * @param properties properties to be looked up
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900359 * @param propertyName the name of the property to look up
360 * @return true when the propertyName is defined and set to true
361 */
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100362 private static boolean isPropertyEnabled(Dictionary<?, ?> properties,
363 String propertyName) {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900364 boolean enabled = false;
365 try {
366 String flag = (String) properties.get(propertyName);
367 if (flag != null) {
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800368 enabled = flag.trim().equals("true");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900369 }
370 } catch (ClassCastException e) {
371 // No propertyName defined.
372 enabled = false;
373 }
374 return enabled;
tomc16656f2014-10-15 18:30:31 -0700375 }
tomc370ebd2014-09-16 01:25:21 -0700376
377 /**
378 * Packet processor responsible for forwarding packets along their paths.
379 */
380 private class ReactivePacketProcessor implements PacketProcessor {
381
382 @Override
383 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700384 // Stop processing if the packet has been handled, since we
385 // can't do any more to it.
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800386 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700387 return;
388 }
tomdc95b8a2014-09-17 08:07:26 -0700389
tomc370ebd2014-09-16 01:25:21 -0700390 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700391 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700392
Jonathan Harte8600eb2015-01-12 10:30:45 -0800393 if (ethPkt == null) {
394 return;
395 }
396
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900397 // Bail if this is deemed to be a control packet.
398 if (isControlPacket(ethPkt)) {
399 return;
400 }
401
402 // Skip IPv6 multicast packet when IPv6 forward is disabled.
403 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800404 return;
405 }
406
tom642b2262014-09-17 13:52:55 -0700407 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700408
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700409 // Do not process link-local addresses in any way.
410 if (id.mac().isLinkLocal()) {
411 return;
412 }
413
Rusty Eddy29acad62015-07-07 19:33:47 -0700414 // Do not process IPv4 multicast packets, let mfwd handle them
415 if (ignoreIpv4McastPackets && ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
416 if (id.mac().isMulticast()) {
417 return;
418 }
419 }
420
tomc370ebd2014-09-16 01:25:21 -0700421 // Do we know who this is for? If not, flood and bail.
422 Host dst = hostService.getHost(id);
423 if (dst == null) {
424 flood(context);
425 return;
426 }
427
428 // Are we on an edge switch that our destination is on? If so,
429 // simply forward out to the destination and bail.
430 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700431 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
432 installRule(context, dst.location().port());
433 }
tomc370ebd2014-09-16 01:25:21 -0700434 return;
435 }
436
437 // Otherwise, get a set of paths that lead from here to the
438 // destination edge switch.
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100439 Set<Path> paths =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700440 topologyService.getPaths(topologyService.currentTopology(),
441 pkt.receivedFrom().deviceId(),
442 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700443 if (paths.isEmpty()) {
444 // If there are no paths, flood and bail.
445 flood(context);
446 return;
447 }
448
449 // Otherwise, pick a path that does not lead back to where we
450 // came from; if no such path, flood and bail.
451 Path path = pickForwardPath(paths, pkt.receivedFrom().port());
452 if (path == null) {
tom642b2262014-09-17 13:52:55 -0700453 log.warn("Doh... don't know where to go... {} -> {} received on {}",
tomc16656f2014-10-15 18:30:31 -0700454 ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
455 pkt.receivedFrom());
tomc370ebd2014-09-16 01:25:21 -0700456 flood(context);
457 return;
458 }
459
460 // Otherwise forward and be done with it.
alshabib7b2748f2014-09-16 20:21:11 -0700461 installRule(context, path.src().port());
tomc370ebd2014-09-16 01:25:21 -0700462 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800463
464 }
465
466 // Indicates whether this is a control packet, e.g. LLDP, BDDP
467 private boolean isControlPacket(Ethernet eth) {
468 short type = eth.getEtherType();
469 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700470 }
471
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800472 // Indicated whether this is an IPv6 multicast packet.
473 private boolean isIpv6Multicast(Ethernet eth) {
474 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
475 }
476
tomc370ebd2014-09-16 01:25:21 -0700477 // Selects a path from the given set that does not lead back to the
478 // specified port.
479 private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) {
480 for (Path path : paths) {
481 if (!path.src().port().equals(notToPort)) {
482 return path;
483 }
484 }
485 return null;
486 }
487
tom642b2262014-09-17 13:52:55 -0700488 // Floods the specified packet if permissible.
tomc370ebd2014-09-16 01:25:21 -0700489 private void flood(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700490 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700491 context.inPacket().receivedFrom())) {
tom642b2262014-09-17 13:52:55 -0700492 packetOut(context, PortNumber.FLOOD);
tomc370ebd2014-09-16 01:25:21 -0700493 } else {
494 context.block();
495 }
496 }
497
tom642b2262014-09-17 13:52:55 -0700498 // Sends a packet out the specified port.
499 private void packetOut(PacketContext context, PortNumber portNumber) {
alshabib010c31d2014-09-26 10:01:12 -0700500 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700501 context.send();
502 }
503
504 // Install a rule forwarding the packet to the specified port.
505 private void installRule(PacketContext context, PortNumber portNumber) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100506 //
507 // We don't support (yet) buffer IDs in the Flow Service so
508 // packet out first.
509 //
510 Ethernet inPkt = context.inPacket().parsed();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700511 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100512
513 // If PacketOutOnly or ARP packet than forward directly to output port
514 if (packetOutOnly || inPkt.getEtherType() == Ethernet.TYPE_ARP) {
515 packetOut(context, portNumber);
516 return;
517 }
518
519 //
520 // If matchDstMacOnly
521 // Create flows matching dstMac only
522 // Else
523 // Create flows with default matching and include configured fields
524 //
525 if (matchDstMacOnly) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700526 selectorBuilder.matchEthDst(inPkt.getDestinationMAC());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100527 } else {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700528 selectorBuilder.matchInPort(context.inPacket().receivedFrom().port())
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800529 .matchEthSrc(inPkt.getSourceMAC())
Jonathan Hart430223a2015-04-22 17:39:02 -0700530 .matchEthDst(inPkt.getDestinationMAC());
alshabib7b2748f2014-09-16 20:21:11 -0700531
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100532 // If configured Match Vlan ID
533 if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700534 selectorBuilder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100535 }
alshabib7b2748f2014-09-16 20:21:11 -0700536
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100537 //
538 // If configured and EtherType is IPv4 - Match IPv4 and
539 // TCP/UDP/ICMP fields
540 //
541 if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
542 IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
543 byte ipv4Protocol = ipv4Packet.getProtocol();
544 Ip4Prefix matchIp4SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700545 Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
546 Ip4Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100547 Ip4Prefix matchIp4DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700548 Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
549 Ip4Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800550 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
Jonathan Hart430223a2015-04-22 17:39:02 -0700551 .matchIPSrc(matchIp4SrcPrefix)
552 .matchIPDst(matchIp4DstPrefix);
alshabib6eb438a2014-10-01 16:39:37 -0700553
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100554 if (matchIpv4Dscp) {
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -0800555 byte dscp = ipv4Packet.getDscp();
556 byte ecn = ipv4Packet.getEcn();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700557 selectorBuilder.matchIPDscp(dscp).matchIPEcn(ecn);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100558 }
559
560 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
561 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700562 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700563 .matchTcpSrc(tcpPacket.getSourcePort())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100564 .matchTcpDst(tcpPacket.getDestinationPort());
565 }
566 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
567 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700568 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700569 .matchUdpSrc(udpPacket.getSourcePort())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100570 .matchUdpDst(udpPacket.getDestinationPort());
571 }
572 if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
573 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700574 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700575 .matchIcmpType(icmpPacket.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100576 .matchIcmpCode(icmpPacket.getIcmpCode());
577 }
578 }
579
580 //
581 // If configured and EtherType is IPv6 - Match IPv6 and
582 // TCP/UDP/ICMP fields
583 //
584 if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
585 IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
586 byte ipv6NextHeader = ipv6Packet.getNextHeader();
587 Ip6Prefix matchIp6SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700588 Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
589 Ip6Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100590 Ip6Prefix matchIp6DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700591 Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
592 Ip6Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800593 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6)
594 .matchIPv6Src(matchIp6SrcPrefix)
Jonathan Hart430223a2015-04-22 17:39:02 -0700595 .matchIPv6Dst(matchIp6DstPrefix);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100596
597 if (matchIpv6FlowLabel) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700598 selectorBuilder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100599 }
600
601 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
602 TCP tcpPacket = (TCP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700603 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700604 .matchTcpSrc(tcpPacket.getSourcePort())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100605 .matchTcpDst(tcpPacket.getDestinationPort());
606 }
607 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
608 UDP udpPacket = (UDP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700609 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700610 .matchUdpSrc(udpPacket.getSourcePort())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100611 .matchUdpDst(udpPacket.getDestinationPort());
612 }
613 if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
614 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700615 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700616 .matchIcmpv6Type(icmp6Packet.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100617 .matchIcmpv6Code(icmp6Packet.getIcmpCode());
618 }
619 }
620 }
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700621 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
622 .setOutput(portNumber)
623 .build();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100624
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700625 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
626 .withSelector(selectorBuilder.build())
627 .withTreatment(treatment)
628 .withPriority(flowPriority)
629 .withFlag(ForwardingObjective.Flag.VERSATILE)
630 .fromApp(appId)
631 .makeTemporary(flowTimeout)
632 .add();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100633
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700634 flowObjectiveService.forward(context.inPacket().receivedFrom().deviceId(),
635 forwardingObjective);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100636
637 //
638 // If packetOutOfppTable
639 // Send packet back to the OpenFlow pipeline to match installed flow
640 // Else
641 // Send packet direction on the appropriate port
642 //
643 if (packetOutOfppTable) {
644 packetOut(context, PortNumber.TABLE);
645 } else {
646 packetOut(context, portNumber);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800647 }
tomc370ebd2014-09-16 01:25:21 -0700648 }
alshabib030111e2014-09-15 15:56:42 -0700649}