blob: 6aa040bfc8650e745561fbcb52428cba88de57a9 [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
Sahil Lelea69534f2015-07-16 15:14:57 -070018import com.google.common.collect.ImmutableSet;
alshabib030111e2014-09-15 15:56:42 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
tomc16656f2014-10-15 18:30:31 -070022import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
alshabib030111e2014-09-15 15:56:42 -070024import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
Jonathan Harte8600eb2015-01-12 10:30:45 -080026import org.onlab.packet.Ethernet;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010027import org.onlab.packet.ICMP;
28import org.onlab.packet.ICMP6;
Jonathan Hart430223a2015-04-22 17:39:02 -070029import org.onlab.packet.IPv4;
30import org.onlab.packet.IPv6;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010031import org.onlab.packet.Ip4Prefix;
32import org.onlab.packet.Ip6Prefix;
Sahil Lelea69534f2015-07-16 15:14:57 -070033import org.onlab.packet.MacAddress;
Jonathan Hart430223a2015-04-22 17:39:02 -070034import org.onlab.packet.TCP;
35import org.onlab.packet.UDP;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010036import org.onlab.packet.VlanId;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070037import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080038import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
Sahil Lelea69534f2015-07-16 15:14:57 -070040import org.onosproject.event.Event;
41import org.onosproject.net.ConnectPoint;
42import org.onosproject.net.DeviceId;
Brian O'Connorabafb502014-12-02 22:26:20 -080043import org.onosproject.net.Host;
44import org.onosproject.net.HostId;
Sahil Lelea69534f2015-07-16 15:14:57 -070045import org.onosproject.net.Link;
Brian O'Connorabafb502014-12-02 22:26:20 -080046import org.onosproject.net.Path;
47import org.onosproject.net.PortNumber;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.DefaultTrafficTreatment;
Sahil Lelea69534f2015-07-16 15:14:57 -070050import org.onosproject.net.flow.FlowEntry;
51import org.onosproject.net.flow.FlowRule;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.flow.FlowRuleService;
53import org.onosproject.net.flow.TrafficSelector;
54import org.onosproject.net.flow.TrafficTreatment;
Sahil Lelea69534f2015-07-16 15:14:57 -070055import org.onosproject.net.flow.criteria.Criterion;
56import org.onosproject.net.flow.criteria.EthCriterion;
57import org.onosproject.net.flow.instructions.Instruction;
58import org.onosproject.net.flow.instructions.Instructions;
Jonathan Hart3b881aa2015-04-22 18:03:50 -070059import org.onosproject.net.flowobjective.DefaultForwardingObjective;
60import org.onosproject.net.flowobjective.FlowObjectiveService;
61import org.onosproject.net.flowobjective.ForwardingObjective;
Brian O'Connorabafb502014-12-02 22:26:20 -080062import org.onosproject.net.host.HostService;
Sahil Lelea69534f2015-07-16 15:14:57 -070063import org.onosproject.net.link.LinkEvent;
Brian O'Connorabafb502014-12-02 22:26:20 -080064import org.onosproject.net.packet.InboundPacket;
65import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080066import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080067import org.onosproject.net.packet.PacketProcessor;
68import org.onosproject.net.packet.PacketService;
Sahil Lelea69534f2015-07-16 15:14:57 -070069import org.onosproject.net.topology.TopologyEvent;
70import org.onosproject.net.topology.TopologyListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080071import org.onosproject.net.topology.TopologyService;
tomc16656f2014-10-15 18:30:31 -070072import org.osgi.service.component.ComponentContext;
tomc370ebd2014-09-16 01:25:21 -070073import org.slf4j.Logger;
alshabib030111e2014-09-15 15:56:42 -070074
Jonathan Hart430223a2015-04-22 17:39:02 -070075import java.util.Dictionary;
Sahil Lele7b961662015-07-20 15:06:04 -070076import java.util.HashMap;
Sahil Lelea69534f2015-07-16 15:14:57 -070077import java.util.List;
Sahil Lele7b961662015-07-20 15:06:04 -070078import java.util.Map;
Sahil Lelea69534f2015-07-16 15:14:57 -070079import java.util.Objects;
Jonathan Hart430223a2015-04-22 17:39:02 -070080import java.util.Set;
81
82import static com.google.common.base.Strings.isNullOrEmpty;
83import static org.slf4j.LoggerFactory.getLogger;
84
tomc370ebd2014-09-16 01:25:21 -070085/**
86 * Sample reactive forwarding application.
87 */
88@Component(immediate = true)
alshabib030111e2014-09-15 15:56:42 -070089public class ReactiveForwarding {
90
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010091 private static final int DEFAULT_TIMEOUT = 10;
92 private static final int DEFAULT_PRIORITY = 10;
alshabibba5ac482014-10-02 17:15:20 -070093
tomc370ebd2014-09-16 01:25:21 -070094 private final Logger log = getLogger(getClass());
95
alshabib030111e2014-09-15 15:56:42 -070096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected TopologyService topologyService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected PacketService packetService;
101
alshabib8aef1ad2014-09-15 17:47:31 -0700102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected HostService hostService;
104
alshabib7b2748f2014-09-16 20:21:11 -0700105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected FlowRuleService flowRuleService;
107
alshabib92c65ad2014-10-08 21:56:05 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700109 protected FlowObjectiveService flowObjectiveService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib92c65ad2014-10-08 21:56:05 -0700112 protected CoreService coreService;
113
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected ComponentConfigService cfgService;
116
tomc370ebd2014-09-16 01:25:21 -0700117 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -0700118
alshabiba68eb962014-09-24 20:34:13 -0700119 private ApplicationId appId;
120
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800121 @Property(name = "packetOutOnly", boolValue = false,
122 label = "Enable packet-out only forwarding; default is false")
123 private boolean packetOutOnly = false;
tomc16656f2014-10-15 18:30:31 -0700124
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100125 @Property(name = "packetOutOfppTable", boolValue = false,
126 label = "Enable first packet forwarding using OFPP_TABLE port " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700127 "instead of PacketOut with actual port; default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100128 private boolean packetOutOfppTable = false;
129
130 @Property(name = "flowTimeout", intValue = DEFAULT_TIMEOUT,
131 label = "Configure Flow Timeout for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700132 "default is 10 sec")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100133 private int flowTimeout = DEFAULT_TIMEOUT;
134
135 @Property(name = "flowPriority", intValue = DEFAULT_PRIORITY,
136 label = "Configure Flow Priority for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700137 "default is 10")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100138 private int flowPriority = DEFAULT_PRIORITY;
139
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900140 @Property(name = "ipv6Forwarding", boolValue = false,
141 label = "Enable IPv6 forwarding; default is false")
142 private boolean ipv6Forwarding = false;
143
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100144 @Property(name = "matchDstMacOnly", boolValue = false,
145 label = "Enable matching Dst Mac Only; default is false")
146 private boolean matchDstMacOnly = false;
147
148 @Property(name = "matchVlanId", boolValue = false,
149 label = "Enable matching Vlan ID; default is false")
150 private boolean matchVlanId = false;
151
152 @Property(name = "matchIpv4Address", boolValue = false,
153 label = "Enable matching IPv4 Addresses; default is false")
154 private boolean matchIpv4Address = false;
155
156 @Property(name = "matchIpv4Dscp", boolValue = false,
157 label = "Enable matching IPv4 DSCP and ECN; default is false")
158 private boolean matchIpv4Dscp = false;
159
160 @Property(name = "matchIpv6Address", boolValue = false,
161 label = "Enable matching IPv6 Addresses; default is false")
162 private boolean matchIpv6Address = false;
163
164 @Property(name = "matchIpv6FlowLabel", boolValue = false,
165 label = "Enable matching IPv6 FlowLabel; default is false")
166 private boolean matchIpv6FlowLabel = false;
167
168 @Property(name = "matchTcpUdpPorts", boolValue = false,
169 label = "Enable matching TCP/UDP ports; default is false")
170 private boolean matchTcpUdpPorts = false;
171
172 @Property(name = "matchIcmpFields", boolValue = false,
173 label = "Enable matching ICMPv4 and ICMPv6 fields; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700174 "default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100175 private boolean matchIcmpFields = false;
176
Sahil Lelea69534f2015-07-16 15:14:57 -0700177
Rusty Eddy29acad62015-07-07 19:33:47 -0700178 @Property(name = "ignoreIPv4Multicast", boolValue = false,
179 label = "Ignore (do not forward) IPv4 multicast packets; default is false")
180 private boolean ignoreIpv4McastPackets = false;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100181
Sahil Lelea69534f2015-07-16 15:14:57 -0700182 private final TopologyListener topologyListener = new InternalTopologyListener();
183
184
alshabib030111e2014-09-15 15:56:42 -0700185 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900186 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700187 cfgService.registerProperties(getClass());
Brian O'Connorabafb502014-12-02 22:26:20 -0800188 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800189
alshabibc274c902014-10-03 14:58:27 -0700190 packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2);
Sahil Lelea69534f2015-07-16 15:14:57 -0700191 topologyService.addListener(topologyListener);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900192 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700193 requestIntercepts();
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800194
Thomas Vachuska8d033672015-07-21 16:15:04 -0700195 log.info("Started", appId.id());
Charles M.C. Chane148de82015-05-06 12:38:21 +0800196 }
197
198 @Deactivate
199 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800200 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700201 withdrawIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800202 flowRuleService.removeFlowRulesById(appId);
203 packetService.removeProcessor(processor);
Sahil Lelea69534f2015-07-16 15:14:57 -0700204 topologyService.removeListener(topologyListener);
Charles M.C. Chane148de82015-05-06 12:38:21 +0800205 processor = null;
206 log.info("Stopped");
207 }
208
209 @Modified
210 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800211 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700212 requestIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800213 }
214
215 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700216 * Request packet in via packet service.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800217 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700218 private void requestIntercepts() {
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800219 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
220 selector.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700221 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100222 selector.matchEthType(Ethernet.TYPE_ARP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700223 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100224
Thomas Vachuska27bee092015-06-23 19:03:10 -0700225 selector.matchEthType(Ethernet.TYPE_IPV6);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100226 if (ipv6Forwarding) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700227 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
228 } else {
229 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100230 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900231 }
232
233 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700234 * Cancel request for packet in via packet service.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700235 */
236 private void withdrawIntercepts() {
237 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
238 selector.matchEthType(Ethernet.TYPE_IPV4);
239 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
240 selector.matchEthType(Ethernet.TYPE_ARP);
241 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
242 selector.matchEthType(Ethernet.TYPE_IPV6);
243 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
244 }
245
246 /**
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900247 * Extracts properties from the component configuration context.
248 *
249 * @param context the component context
250 */
251 private void readComponentConfiguration(ComponentContext context) {
252 Dictionary<?, ?> properties = context.getProperties();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100253 boolean packetOutOnlyEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700254 isPropertyEnabled(properties, "packetOutOnly");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900255 if (packetOutOnly != packetOutOnlyEnabled) {
256 packetOutOnly = packetOutOnlyEnabled;
257 log.info("Configured. Packet-out only forwarding is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700258 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700259 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100260 boolean packetOutOfppTableEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700261 isPropertyEnabled(properties, "packetOutOfppTable");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100262 if (packetOutOfppTable != packetOutOfppTableEnabled) {
263 packetOutOfppTable = packetOutOfppTableEnabled;
264 log.info("Configured. Forwarding using OFPP_TABLE port is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700265 packetOutOfppTable ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100266 }
267 boolean ipv6ForwardingEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700268 isPropertyEnabled(properties, "ipv6Forwarding");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900269 if (ipv6Forwarding != ipv6ForwardingEnabled) {
270 ipv6Forwarding = ipv6ForwardingEnabled;
271 log.info("Configured. IPv6 forwarding is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700272 ipv6Forwarding ? "enabled" : "disabled");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900273 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100274 boolean matchDstMacOnlyEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700275 isPropertyEnabled(properties, "matchDstMacOnly");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100276 if (matchDstMacOnly != matchDstMacOnlyEnabled) {
277 matchDstMacOnly = matchDstMacOnlyEnabled;
278 log.info("Configured. Match Dst MAC Only is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700279 matchDstMacOnly ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100280 }
281 boolean matchVlanIdEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700282 isPropertyEnabled(properties, "matchVlanId");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100283 if (matchVlanId != matchVlanIdEnabled) {
284 matchVlanId = matchVlanIdEnabled;
285 log.info("Configured. Matching Vlan ID is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700286 matchVlanId ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100287 }
288 boolean matchIpv4AddressEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700289 isPropertyEnabled(properties, "matchIpv4Address");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100290 if (matchIpv4Address != matchIpv4AddressEnabled) {
291 matchIpv4Address = matchIpv4AddressEnabled;
292 log.info("Configured. Matching IPv4 Addresses is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700293 matchIpv4Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100294 }
295 boolean matchIpv4DscpEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700296 isPropertyEnabled(properties, "matchIpv4Dscp");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100297 if (matchIpv4Dscp != matchIpv4DscpEnabled) {
298 matchIpv4Dscp = matchIpv4DscpEnabled;
299 log.info("Configured. Matching IPv4 DSCP and ECN is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700300 matchIpv4Dscp ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100301 }
302 boolean matchIpv6AddressEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700303 isPropertyEnabled(properties, "matchIpv6Address");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100304 if (matchIpv6Address != matchIpv6AddressEnabled) {
305 matchIpv6Address = matchIpv6AddressEnabled;
306 log.info("Configured. Matching IPv6 Addresses is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700307 matchIpv6Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100308 }
309 boolean matchIpv6FlowLabelEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700310 isPropertyEnabled(properties, "matchIpv6FlowLabel");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100311 if (matchIpv6FlowLabel != matchIpv6FlowLabelEnabled) {
312 matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
313 log.info("Configured. Matching IPv6 FlowLabel is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700314 matchIpv6FlowLabel ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100315 }
316 boolean matchTcpUdpPortsEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700317 isPropertyEnabled(properties, "matchTcpUdpPorts");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100318 if (matchTcpUdpPorts != matchTcpUdpPortsEnabled) {
319 matchTcpUdpPorts = matchTcpUdpPortsEnabled;
320 log.info("Configured. Matching TCP/UDP fields is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700321 matchTcpUdpPorts ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100322 }
323 boolean matchIcmpFieldsEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700324 isPropertyEnabled(properties, "matchIcmpFields");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100325 if (matchIcmpFields != matchIcmpFieldsEnabled) {
326 matchIcmpFields = matchIcmpFieldsEnabled;
327 log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700328 matchIcmpFields ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100329 }
330 Integer flowTimeoutConfigured =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700331 getIntegerProperty(properties, "flowTimeout");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100332 if (flowTimeoutConfigured == null) {
333 log.info("Flow Timeout is not configured, default value is {}",
334 flowTimeout);
335 } else {
336 flowTimeout = flowTimeoutConfigured;
337 log.info("Configured. Flow Timeout is configured to {}",
338 flowTimeout, " seconds");
339 }
340 Integer flowPriorityConfigured =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700341 getIntegerProperty(properties, "flowPriority");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100342 if (flowPriorityConfigured == null) {
343 log.info("Flow Priority is not configured, default value is {}",
344 flowPriority);
345 } else {
346 flowPriority = flowPriorityConfigured;
347 log.info("Configured. Flow Priority is configured to {}",
348 flowPriority);
349 }
Rusty Eddy29acad62015-07-07 19:33:47 -0700350
351 boolean ignoreIpv4McastPacketsEnabled =
352 isPropertyEnabled(properties, "ignoreIpv4McastPackets");
353 if (ignoreIpv4McastPackets != ignoreIpv4McastPacketsEnabled) {
354 ignoreIpv4McastPackets = ignoreIpv4McastPacketsEnabled;
355 log.info("Configured. Ignore IPv4 multicast packets is {}",
Thomas Vachuska8d033672015-07-21 16:15:04 -0700356 ignoreIpv4McastPackets ? "enabled" : "disabled");
Rusty Eddy29acad62015-07-07 19:33:47 -0700357 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100358 }
359
360 /**
361 * Get Integer property from the propertyName
362 * Return null if propertyName is not found.
363 *
Thomas Vachuska27bee092015-06-23 19:03:10 -0700364 * @param properties properties to be looked up
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100365 * @param propertyName the name of the property to look up
366 * @return value when the propertyName is defined or return null
367 */
368 private static Integer getIntegerProperty(Dictionary<?, ?> properties,
369 String propertyName) {
370 Integer value = null;
371 try {
372 String s = (String) properties.get(propertyName);
373 value = isNullOrEmpty(s) ? value : Integer.parseInt(s.trim());
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800374 } catch (NumberFormatException | ClassCastException e) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100375 value = null;
376 }
377 return value;
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900378 }
379
380 /**
381 * Check property name is defined and set to true.
382 *
Thomas Vachuska27bee092015-06-23 19:03:10 -0700383 * @param properties properties to be looked up
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900384 * @param propertyName the name of the property to look up
385 * @return true when the propertyName is defined and set to true
386 */
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100387 private static boolean isPropertyEnabled(Dictionary<?, ?> properties,
388 String propertyName) {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900389 boolean enabled = false;
390 try {
391 String flag = (String) properties.get(propertyName);
392 if (flag != null) {
Ayaka Koshibe8851ed92015-01-22 12:07:24 -0800393 enabled = flag.trim().equals("true");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900394 }
395 } catch (ClassCastException e) {
396 // No propertyName defined.
397 enabled = false;
398 }
399 return enabled;
tomc16656f2014-10-15 18:30:31 -0700400 }
tomc370ebd2014-09-16 01:25:21 -0700401
402 /**
403 * Packet processor responsible for forwarding packets along their paths.
404 */
405 private class ReactivePacketProcessor implements PacketProcessor {
406
407 @Override
408 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700409 // Stop processing if the packet has been handled, since we
410 // can't do any more to it.
Sahil Lelea69534f2015-07-16 15:14:57 -0700411
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800412 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700413 return;
414 }
tomdc95b8a2014-09-17 08:07:26 -0700415
tomc370ebd2014-09-16 01:25:21 -0700416 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700417 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700418
Jonathan Harte8600eb2015-01-12 10:30:45 -0800419 if (ethPkt == null) {
420 return;
421 }
422
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900423 // Bail if this is deemed to be a control packet.
424 if (isControlPacket(ethPkt)) {
425 return;
426 }
427
428 // Skip IPv6 multicast packet when IPv6 forward is disabled.
429 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800430 return;
431 }
432
tom642b2262014-09-17 13:52:55 -0700433 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700434
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700435 // Do not process link-local addresses in any way.
436 if (id.mac().isLinkLocal()) {
437 return;
438 }
439
Rusty Eddy29acad62015-07-07 19:33:47 -0700440 // Do not process IPv4 multicast packets, let mfwd handle them
441 if (ignoreIpv4McastPackets && ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
442 if (id.mac().isMulticast()) {
443 return;
444 }
445 }
446
tomc370ebd2014-09-16 01:25:21 -0700447 // Do we know who this is for? If not, flood and bail.
448 Host dst = hostService.getHost(id);
449 if (dst == null) {
450 flood(context);
451 return;
452 }
453
454 // Are we on an edge switch that our destination is on? If so,
455 // simply forward out to the destination and bail.
456 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700457 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
458 installRule(context, dst.location().port());
459 }
tomc370ebd2014-09-16 01:25:21 -0700460 return;
461 }
462
463 // Otherwise, get a set of paths that lead from here to the
464 // destination edge switch.
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100465 Set<Path> paths =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700466 topologyService.getPaths(topologyService.currentTopology(),
467 pkt.receivedFrom().deviceId(),
468 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700469 if (paths.isEmpty()) {
470 // If there are no paths, flood and bail.
471 flood(context);
472 return;
473 }
474
475 // Otherwise, pick a path that does not lead back to where we
476 // came from; if no such path, flood and bail.
477 Path path = pickForwardPath(paths, pkt.receivedFrom().port());
478 if (path == null) {
tom642b2262014-09-17 13:52:55 -0700479 log.warn("Doh... don't know where to go... {} -> {} received on {}",
tomc16656f2014-10-15 18:30:31 -0700480 ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
481 pkt.receivedFrom());
tomc370ebd2014-09-16 01:25:21 -0700482 flood(context);
483 return;
484 }
485
486 // Otherwise forward and be done with it.
alshabib7b2748f2014-09-16 20:21:11 -0700487 installRule(context, path.src().port());
tomc370ebd2014-09-16 01:25:21 -0700488 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800489
490 }
491
492 // Indicates whether this is a control packet, e.g. LLDP, BDDP
493 private boolean isControlPacket(Ethernet eth) {
494 short type = eth.getEtherType();
495 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700496 }
497
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800498 // Indicated whether this is an IPv6 multicast packet.
499 private boolean isIpv6Multicast(Ethernet eth) {
500 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
501 }
502
tomc370ebd2014-09-16 01:25:21 -0700503 // Selects a path from the given set that does not lead back to the
504 // specified port.
505 private Path pickForwardPath(Set<Path> paths, PortNumber notToPort) {
506 for (Path path : paths) {
507 if (!path.src().port().equals(notToPort)) {
508 return path;
509 }
510 }
511 return null;
512 }
513
tom642b2262014-09-17 13:52:55 -0700514 // Floods the specified packet if permissible.
tomc370ebd2014-09-16 01:25:21 -0700515 private void flood(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700516 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700517 context.inPacket().receivedFrom())) {
tom642b2262014-09-17 13:52:55 -0700518 packetOut(context, PortNumber.FLOOD);
tomc370ebd2014-09-16 01:25:21 -0700519 } else {
520 context.block();
521 }
522 }
523
tom642b2262014-09-17 13:52:55 -0700524 // Sends a packet out the specified port.
525 private void packetOut(PacketContext context, PortNumber portNumber) {
alshabib010c31d2014-09-26 10:01:12 -0700526 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700527 context.send();
528 }
529
530 // Install a rule forwarding the packet to the specified port.
531 private void installRule(PacketContext context, PortNumber portNumber) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100532 //
533 // We don't support (yet) buffer IDs in the Flow Service so
534 // packet out first.
535 //
536 Ethernet inPkt = context.inPacket().parsed();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700537 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100538
539 // If PacketOutOnly or ARP packet than forward directly to output port
540 if (packetOutOnly || inPkt.getEtherType() == Ethernet.TYPE_ARP) {
541 packetOut(context, portNumber);
542 return;
543 }
544
545 //
546 // If matchDstMacOnly
547 // Create flows matching dstMac only
548 // Else
549 // Create flows with default matching and include configured fields
550 //
551 if (matchDstMacOnly) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700552 selectorBuilder.matchEthDst(inPkt.getDestinationMAC());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100553 } else {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700554 selectorBuilder.matchInPort(context.inPacket().receivedFrom().port())
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800555 .matchEthSrc(inPkt.getSourceMAC())
Jonathan Hart430223a2015-04-22 17:39:02 -0700556 .matchEthDst(inPkt.getDestinationMAC());
alshabib7b2748f2014-09-16 20:21:11 -0700557
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100558 // If configured Match Vlan ID
559 if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700560 selectorBuilder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100561 }
alshabib7b2748f2014-09-16 20:21:11 -0700562
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100563 //
564 // If configured and EtherType is IPv4 - Match IPv4 and
565 // TCP/UDP/ICMP fields
566 //
567 if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
568 IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
569 byte ipv4Protocol = ipv4Packet.getProtocol();
570 Ip4Prefix matchIp4SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700571 Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
572 Ip4Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100573 Ip4Prefix matchIp4DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700574 Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
575 Ip4Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800576 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
Jonathan Hart430223a2015-04-22 17:39:02 -0700577 .matchIPSrc(matchIp4SrcPrefix)
578 .matchIPDst(matchIp4DstPrefix);
alshabib6eb438a2014-10-01 16:39:37 -0700579
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100580 if (matchIpv4Dscp) {
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -0800581 byte dscp = ipv4Packet.getDscp();
582 byte ecn = ipv4Packet.getEcn();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700583 selectorBuilder.matchIPDscp(dscp).matchIPEcn(ecn);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100584 }
585
586 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
587 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700588 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700589 .matchTcpSrc(tcpPacket.getSourcePort())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100590 .matchTcpDst(tcpPacket.getDestinationPort());
591 }
592 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
593 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700594 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700595 .matchUdpSrc(udpPacket.getSourcePort())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100596 .matchUdpDst(udpPacket.getDestinationPort());
597 }
598 if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
599 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700600 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700601 .matchIcmpType(icmpPacket.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100602 .matchIcmpCode(icmpPacket.getIcmpCode());
603 }
604 }
605
606 //
607 // If configured and EtherType is IPv6 - Match IPv6 and
608 // TCP/UDP/ICMP fields
609 //
610 if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
611 IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
612 byte ipv6NextHeader = ipv6Packet.getNextHeader();
613 Ip6Prefix matchIp6SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700614 Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
615 Ip6Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100616 Ip6Prefix matchIp6DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700617 Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
618 Ip6Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800619 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6)
620 .matchIPv6Src(matchIp6SrcPrefix)
Jonathan Hart430223a2015-04-22 17:39:02 -0700621 .matchIPv6Dst(matchIp6DstPrefix);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100622
623 if (matchIpv6FlowLabel) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700624 selectorBuilder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100625 }
626
627 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
628 TCP tcpPacket = (TCP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700629 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700630 .matchTcpSrc(tcpPacket.getSourcePort())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100631 .matchTcpDst(tcpPacket.getDestinationPort());
632 }
633 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
634 UDP udpPacket = (UDP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700635 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700636 .matchUdpSrc(udpPacket.getSourcePort())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100637 .matchUdpDst(udpPacket.getDestinationPort());
638 }
639 if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
640 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700641 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700642 .matchIcmpv6Type(icmp6Packet.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100643 .matchIcmpv6Code(icmp6Packet.getIcmpCode());
644 }
645 }
646 }
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700647 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
648 .setOutput(portNumber)
649 .build();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100650
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700651 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
652 .withSelector(selectorBuilder.build())
653 .withTreatment(treatment)
654 .withPriority(flowPriority)
655 .withFlag(ForwardingObjective.Flag.VERSATILE)
656 .fromApp(appId)
657 .makeTemporary(flowTimeout)
658 .add();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100659
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700660 flowObjectiveService.forward(context.inPacket().receivedFrom().deviceId(),
661 forwardingObjective);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100662
663 //
664 // If packetOutOfppTable
665 // Send packet back to the OpenFlow pipeline to match installed flow
666 // Else
667 // Send packet direction on the appropriate port
668 //
669 if (packetOutOfppTable) {
670 packetOut(context, PortNumber.TABLE);
671 } else {
672 packetOut(context, portNumber);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800673 }
tomc370ebd2014-09-16 01:25:21 -0700674 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700675
676 private class InternalTopologyListener implements TopologyListener {
677 @Override
678 public void event(TopologyEvent event) {
679 List<Event> reasons = event.reasons();
680 if (reasons != null) {
681 reasons.forEach(re -> {
682 if (re instanceof LinkEvent) {
683 LinkEvent le = (LinkEvent) re;
684 if (le.type() == LinkEvent.Type.LINK_REMOVED) {
685 fixBlackhole(le.subject().src());
686 }
687 }
688 });
689 }
690 }
691 }
692
693 private void fixBlackhole(ConnectPoint egress) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700694 Set<FlowEntry> rules = getFlowRulesFrom(egress);
Sahil Lelea69534f2015-07-16 15:14:57 -0700695 Set<SrcDstPair> pairs = findSrcDstPairs(rules);
696
Sahil Lele7b961662015-07-20 15:06:04 -0700697 Map<DeviceId, Set<Path>> srcPaths = new HashMap<>();
698
Thomas Vachuska8d033672015-07-21 16:15:04 -0700699 for (SrcDstPair sd : pairs) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700700 // get the edge deviceID for the src host
701 DeviceId srcId = hostService.getHost(HostId.hostId(sd.src)).location().deviceId();
702 DeviceId dstId = hostService.getHost(HostId.hostId(sd.dst)).location().deviceId();
703 log.trace("SRC ID is " + srcId + ", DST ID is " + dstId);
704
705 cleanFlowRules(sd, egress.deviceId());
706
Sahil Lele7b961662015-07-20 15:06:04 -0700707 Set<Path> shortestPaths = srcPaths.get(srcId);
708 if (shortestPaths == null) {
709 shortestPaths = topologyService.getPaths(topologyService.currentTopology(), egress.deviceId(), srcId);
710 srcPaths.put(srcId, shortestPaths);
711 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700712 backTrackBadNodes(shortestPaths, dstId, sd);
713 }
714 }
715
716 // Backtracks from link down event to remove flows that lead to blackhole
717 private void backTrackBadNodes(Set<Path> shortestPaths, DeviceId dstId, SrcDstPair sd) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700718 for (Path p : shortestPaths) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700719 List<Link> pathLinks = p.links();
720 for (int i = 0; i < pathLinks.size(); i = i + 1) {
721 Link curLink = pathLinks.get(i);
722 DeviceId curDevice = curLink.src().deviceId();
Sahil Lelea69534f2015-07-16 15:14:57 -0700723
724 // skipping the first link because this link's src has already been pruned beforehand
725 if (i != 0) {
726 cleanFlowRules(sd, curDevice);
727 }
728
Thomas Vachuska8d033672015-07-21 16:15:04 -0700729 Set<Path> pathsFromCurDevice =
730 topologyService.getPaths(topologyService.currentTopology(),
731 curDevice, dstId);
Sahil Lelea69534f2015-07-16 15:14:57 -0700732 if (pickForwardPath(pathsFromCurDevice, curLink.src().port()) != null) {
733 break;
734 } else {
735 if (i + 1 == pathLinks.size()) {
736 cleanFlowRules(sd, curLink.dst().deviceId());
737 }
738 }
739 }
740 }
741 }
742
743 // Removes flow rules off specified device with specific SrcDstPair
744 private void cleanFlowRules(SrcDstPair pair, DeviceId id) {
745 log.trace("Searching for flow rules to remove from: " + id);
746 log.trace("Removing flows w/ SRC=" + pair.src + ", DST=" + pair.dst);
747 for (FlowEntry r : flowRuleService.getFlowEntries(id)) {
748 boolean matchesSrc = false, matchesDst = false;
749 for (Instruction i : r.treatment().allInstructions()) {
750 if (i.type() == Instruction.Type.OUTPUT) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700751 // if the flow has matching src and dst
Sahil Lelea69534f2015-07-16 15:14:57 -0700752 for (Criterion cr : r.selector().criteria()) {
753 if (cr.type() == Criterion.Type.ETH_DST) {
754 if (((EthCriterion) cr).mac().equals(pair.dst)) {
755 matchesDst = true;
756 }
757 } else if (cr.type() == Criterion.Type.ETH_SRC) {
758 if (((EthCriterion) cr).mac().equals(pair.src)) {
759 matchesSrc = true;
760 }
761 }
762 }
763 }
764 }
765 if (matchesDst && matchesSrc) {
766 log.trace("Removed flow rule from device: " + id);
767 flowRuleService.removeFlowRules((FlowRule) r);
768 }
769 }
770
771 }
772
773 // Returns a set of src/dst MAC pairs extracted from the specified set of flow entries
774 private Set<SrcDstPair> findSrcDstPairs(Set<FlowEntry> rules) {
775 ImmutableSet.Builder<SrcDstPair> builder = ImmutableSet.builder();
Thomas Vachuska8d033672015-07-21 16:15:04 -0700776 for (FlowEntry r : rules) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700777 MacAddress src = null, dst = null;
Thomas Vachuska8d033672015-07-21 16:15:04 -0700778 for (Criterion cr : r.selector().criteria()) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700779 if (cr.type() == Criterion.Type.ETH_DST) {
780 dst = ((EthCriterion) cr).mac();
781 } else if (cr.type() == Criterion.Type.ETH_SRC) {
782 src = ((EthCriterion) cr).mac();
783 }
784 }
785 builder.add(new SrcDstPair(src, dst));
786 }
787 return builder.build();
788 }
789
790 // Returns set of flowEntries which were created by this application and which egress from the
791 // specified connection port
792 private Set<FlowEntry> getFlowRulesFrom(ConnectPoint egress) {
793 ImmutableSet.Builder<FlowEntry> builder = ImmutableSet.builder();
794 flowRuleService.getFlowEntries(egress.deviceId()).forEach(r -> {
795 if (r.appId() == appId.id()) {
796 r.treatment().allInstructions().forEach(i -> {
797 if (i.type() == Instruction.Type.OUTPUT) {
798 if (((Instructions.OutputInstruction) i).port().equals(egress.port())) {
799 builder.add(r);
800 }
801 }
802 });
803 }
804 });
805
806 return builder.build();
807 }
808
809 // Wrapper class for a source and destination pair of MAC addresses
810 private final class SrcDstPair {
811 final MacAddress src;
812 final MacAddress dst;
813
814 private SrcDstPair(MacAddress src, MacAddress dst) {
815 this.src = src;
816 this.dst = dst;
817 }
818
819 @Override
820 public boolean equals(Object o) {
821 if (this == o) {
822 return true;
823 }
824 if (o == null || getClass() != o.getClass()) {
825 return false;
826 }
827 SrcDstPair that = (SrcDstPair) o;
828 return Objects.equals(src, that.src) &&
829 Objects.equals(dst, that.dst);
830 }
831
832 @Override
833 public int hashCode() {
834 return Objects.hash(src, dst);
835 }
836 }
alshabib030111e2014-09-15 15:56:42 -0700837}