blob: 7ab26d31e2343137ec29787fd6f1e29898cd1016 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present 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;
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070035import org.onlab.packet.TpPort;
Jonathan Hart430223a2015-04-22 17:39:02 -070036import org.onlab.packet.UDP;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010037import org.onlab.packet.VlanId;
maojianwei8bf77b72016-02-15 15:18:24 +080038import org.onlab.util.Tools;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070039import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080040import org.onosproject.core.ApplicationId;
41import org.onosproject.core.CoreService;
Sahil Lelea69534f2015-07-16 15:14:57 -070042import org.onosproject.event.Event;
43import org.onosproject.net.ConnectPoint;
44import org.onosproject.net.DeviceId;
Brian O'Connorabafb502014-12-02 22:26:20 -080045import org.onosproject.net.Host;
46import org.onosproject.net.HostId;
Sahil Lelea69534f2015-07-16 15:14:57 -070047import org.onosproject.net.Link;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.Path;
49import org.onosproject.net.PortNumber;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.flow.DefaultTrafficSelector;
51import org.onosproject.net.flow.DefaultTrafficTreatment;
Sahil Lelea69534f2015-07-16 15:14:57 -070052import org.onosproject.net.flow.FlowEntry;
53import org.onosproject.net.flow.FlowRule;
Brian O'Connorabafb502014-12-02 22:26:20 -080054import org.onosproject.net.flow.FlowRuleService;
55import org.onosproject.net.flow.TrafficSelector;
56import org.onosproject.net.flow.TrafficTreatment;
Sahil Lelea69534f2015-07-16 15:14:57 -070057import org.onosproject.net.flow.criteria.Criterion;
58import org.onosproject.net.flow.criteria.EthCriterion;
59import org.onosproject.net.flow.instructions.Instruction;
60import org.onosproject.net.flow.instructions.Instructions;
Jonathan Hart3b881aa2015-04-22 18:03:50 -070061import org.onosproject.net.flowobjective.DefaultForwardingObjective;
62import org.onosproject.net.flowobjective.FlowObjectiveService;
63import org.onosproject.net.flowobjective.ForwardingObjective;
Brian O'Connorabafb502014-12-02 22:26:20 -080064import org.onosproject.net.host.HostService;
Sahil Lelea69534f2015-07-16 15:14:57 -070065import org.onosproject.net.link.LinkEvent;
Brian O'Connorabafb502014-12-02 22:26:20 -080066import org.onosproject.net.packet.InboundPacket;
67import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080068import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080069import org.onosproject.net.packet.PacketProcessor;
70import org.onosproject.net.packet.PacketService;
Sahil Lelea69534f2015-07-16 15:14:57 -070071import org.onosproject.net.topology.TopologyEvent;
72import org.onosproject.net.topology.TopologyListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080073import org.onosproject.net.topology.TopologyService;
tomc16656f2014-10-15 18:30:31 -070074import org.osgi.service.component.ComponentContext;
tomc370ebd2014-09-16 01:25:21 -070075import org.slf4j.Logger;
alshabib030111e2014-09-15 15:56:42 -070076
Jonathan Hart430223a2015-04-22 17:39:02 -070077import java.util.Dictionary;
Sahil Lele7b961662015-07-20 15:06:04 -070078import java.util.HashMap;
Sahil Lelea69534f2015-07-16 15:14:57 -070079import java.util.List;
Sahil Lele7b961662015-07-20 15:06:04 -070080import java.util.Map;
Sahil Lelea69534f2015-07-16 15:14:57 -070081import java.util.Objects;
Jonathan Hart430223a2015-04-22 17:39:02 -070082import java.util.Set;
83
Jonathan Hart430223a2015-04-22 17:39:02 -070084import static org.slf4j.LoggerFactory.getLogger;
85
tomc370ebd2014-09-16 01:25:21 -070086/**
87 * Sample reactive forwarding application.
88 */
89@Component(immediate = true)
alshabib030111e2014-09-15 15:56:42 -070090public class ReactiveForwarding {
91
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010092 private static final int DEFAULT_TIMEOUT = 10;
93 private static final int DEFAULT_PRIORITY = 10;
alshabibba5ac482014-10-02 17:15:20 -070094
tomc370ebd2014-09-16 01:25:21 -070095 private final Logger log = getLogger(getClass());
96
alshabib030111e2014-09-15 15:56:42 -070097 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected TopologyService topologyService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected PacketService packetService;
102
alshabib8aef1ad2014-09-15 17:47:31 -0700103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected HostService hostService;
105
alshabib7b2748f2014-09-16 20:21:11 -0700106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected FlowRuleService flowRuleService;
108
alshabib92c65ad2014-10-08 21:56:05 -0700109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700110 protected FlowObjectiveService flowObjectiveService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib92c65ad2014-10-08 21:56:05 -0700113 protected CoreService coreService;
114
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected ComponentConfigService cfgService;
117
tomc370ebd2014-09-16 01:25:21 -0700118 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -0700119
alshabiba68eb962014-09-24 20:34:13 -0700120 private ApplicationId appId;
121
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800122 @Property(name = "packetOutOnly", boolValue = false,
123 label = "Enable packet-out only forwarding; default is false")
124 private boolean packetOutOnly = false;
tomc16656f2014-10-15 18:30:31 -0700125
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100126 @Property(name = "packetOutOfppTable", boolValue = false,
127 label = "Enable first packet forwarding using OFPP_TABLE port " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700128 "instead of PacketOut with actual port; default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100129 private boolean packetOutOfppTable = false;
130
131 @Property(name = "flowTimeout", intValue = DEFAULT_TIMEOUT,
132 label = "Configure Flow Timeout for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700133 "default is 10 sec")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100134 private int flowTimeout = DEFAULT_TIMEOUT;
135
136 @Property(name = "flowPriority", intValue = DEFAULT_PRIORITY,
137 label = "Configure Flow Priority for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700138 "default is 10")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100139 private int flowPriority = DEFAULT_PRIORITY;
140
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900141 @Property(name = "ipv6Forwarding", boolValue = false,
142 label = "Enable IPv6 forwarding; default is false")
143 private boolean ipv6Forwarding = false;
144
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100145 @Property(name = "matchDstMacOnly", boolValue = false,
146 label = "Enable matching Dst Mac Only; default is false")
147 private boolean matchDstMacOnly = false;
148
149 @Property(name = "matchVlanId", boolValue = false,
150 label = "Enable matching Vlan ID; default is false")
151 private boolean matchVlanId = false;
152
153 @Property(name = "matchIpv4Address", boolValue = false,
154 label = "Enable matching IPv4 Addresses; default is false")
155 private boolean matchIpv4Address = false;
156
157 @Property(name = "matchIpv4Dscp", boolValue = false,
158 label = "Enable matching IPv4 DSCP and ECN; default is false")
159 private boolean matchIpv4Dscp = false;
160
161 @Property(name = "matchIpv6Address", boolValue = false,
162 label = "Enable matching IPv6 Addresses; default is false")
163 private boolean matchIpv6Address = false;
164
165 @Property(name = "matchIpv6FlowLabel", boolValue = false,
166 label = "Enable matching IPv6 FlowLabel; default is false")
167 private boolean matchIpv6FlowLabel = false;
168
169 @Property(name = "matchTcpUdpPorts", boolValue = false,
170 label = "Enable matching TCP/UDP ports; default is false")
171 private boolean matchTcpUdpPorts = false;
172
173 @Property(name = "matchIcmpFields", boolValue = false,
174 label = "Enable matching ICMPv4 and ICMPv6 fields; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700175 "default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100176 private boolean matchIcmpFields = false;
177
Sahil Lelea69534f2015-07-16 15:14:57 -0700178
Rusty Eddy29acad62015-07-07 19:33:47 -0700179 @Property(name = "ignoreIPv4Multicast", boolValue = false,
180 label = "Ignore (do not forward) IPv4 multicast packets; default is false")
181 private boolean ignoreIpv4McastPackets = false;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100182
Sahil Lelea69534f2015-07-16 15:14:57 -0700183 private final TopologyListener topologyListener = new InternalTopologyListener();
184
185
alshabib030111e2014-09-15 15:56:42 -0700186 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900187 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700188 cfgService.registerProperties(getClass());
Brian O'Connorabafb502014-12-02 22:26:20 -0800189 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800190
Brian O'Connor3b783262015-07-29 17:49:24 -0700191 packetService.addProcessor(processor, PacketProcessor.director(2));
Sahil Lelea69534f2015-07-16 15:14:57 -0700192 topologyService.addListener(topologyListener);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900193 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700194 requestIntercepts();
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800195
Thomas Vachuska8d033672015-07-21 16:15:04 -0700196 log.info("Started", appId.id());
Charles M.C. Chane148de82015-05-06 12:38:21 +0800197 }
198
199 @Deactivate
200 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800201 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700202 withdrawIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800203 flowRuleService.removeFlowRulesById(appId);
204 packetService.removeProcessor(processor);
Sahil Lelea69534f2015-07-16 15:14:57 -0700205 topologyService.removeListener(topologyListener);
Charles M.C. Chane148de82015-05-06 12:38:21 +0800206 processor = null;
207 log.info("Stopped");
208 }
209
210 @Modified
211 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800212 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700213 requestIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800214 }
215
216 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700217 * Request packet in via packet service.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800218 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700219 private void requestIntercepts() {
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800220 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
221 selector.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700222 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100223 selector.matchEthType(Ethernet.TYPE_ARP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700224 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100225
Thomas Vachuska27bee092015-06-23 19:03:10 -0700226 selector.matchEthType(Ethernet.TYPE_IPV6);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100227 if (ipv6Forwarding) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700228 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
229 } else {
230 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100231 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900232 }
233
234 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700235 * Cancel request for packet in via packet service.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700236 */
237 private void withdrawIntercepts() {
238 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
239 selector.matchEthType(Ethernet.TYPE_IPV4);
240 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
241 selector.matchEthType(Ethernet.TYPE_ARP);
242 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
243 selector.matchEthType(Ethernet.TYPE_IPV6);
244 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
245 }
246
247 /**
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900248 * Extracts properties from the component configuration context.
249 *
250 * @param context the component context
251 */
252 private void readComponentConfiguration(ComponentContext context) {
253 Dictionary<?, ?> properties = context.getProperties();
Jian Lid9b5f552016-03-11 18:15:31 -0800254
255 Boolean packetOutOnlyEnabled =
256 Tools.isPropertyEnabled(properties, "packetOutOnly");
257 if (packetOutOnlyEnabled == null) {
258 log.info("Packet-out is not configured, " +
259 "using current value of {}", packetOutOnly);
260 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900261 packetOutOnly = packetOutOnlyEnabled;
262 log.info("Configured. Packet-out only forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800263 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700264 }
Jian Lid9b5f552016-03-11 18:15:31 -0800265
266 Boolean packetOutOfppTableEnabled =
267 Tools.isPropertyEnabled(properties, "packetOutOfppTable");
268 if (packetOutOfppTableEnabled == null) {
269 log.info("OFPP_TABLE port is not configured, " +
270 "using current value of {}", packetOutOfppTable);
271 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100272 packetOutOfppTable = packetOutOfppTableEnabled;
273 log.info("Configured. Forwarding using OFPP_TABLE port is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800274 packetOutOfppTable ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100275 }
Jian Lid9b5f552016-03-11 18:15:31 -0800276
277 Boolean ipv6ForwardingEnabled =
278 Tools.isPropertyEnabled(properties, "ipv6Forwarding");
279 if (ipv6ForwardingEnabled == null) {
280 log.info("IPv6 forwarding is not configured, " +
281 "using current value of {}", ipv6Forwarding);
282 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900283 ipv6Forwarding = ipv6ForwardingEnabled;
284 log.info("Configured. IPv6 forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800285 ipv6Forwarding ? "enabled" : "disabled");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900286 }
Jian Lid9b5f552016-03-11 18:15:31 -0800287
288 Boolean matchDstMacOnlyEnabled =
289 Tools.isPropertyEnabled(properties, "matchDstMacOnly");
290 if (matchDstMacOnlyEnabled == null) {
291 log.info("Match Dst MAC is not configured, " +
292 "using current value of {}", matchDstMacOnly);
293 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100294 matchDstMacOnly = matchDstMacOnlyEnabled;
295 log.info("Configured. Match Dst MAC Only is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800296 matchDstMacOnly ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100297 }
Jian Lid9b5f552016-03-11 18:15:31 -0800298
299 Boolean matchVlanIdEnabled =
300 Tools.isPropertyEnabled(properties, "matchVlanId");
301 if (matchVlanIdEnabled == null) {
302 log.info("Matching Vlan ID is not configured, " +
303 "using current value of {}", matchVlanId);
304 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100305 matchVlanId = matchVlanIdEnabled;
306 log.info("Configured. Matching Vlan ID is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800307 matchVlanId ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100308 }
Jian Lid9b5f552016-03-11 18:15:31 -0800309
310 Boolean matchIpv4AddressEnabled =
311 Tools.isPropertyEnabled(properties, "matchIpv4Address");
312 if (matchIpv4AddressEnabled == null) {
313 log.info("Matching IPv4 Address is not configured, " +
314 "using current value of {}", matchIpv4Address);
315 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100316 matchIpv4Address = matchIpv4AddressEnabled;
317 log.info("Configured. Matching IPv4 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800318 matchIpv4Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100319 }
Jian Lid9b5f552016-03-11 18:15:31 -0800320
321 Boolean matchIpv4DscpEnabled =
322 Tools.isPropertyEnabled(properties, "matchIpv4Dscp");
323 if (matchIpv4DscpEnabled == null) {
324 log.info("Matching IPv4 DSCP and ECN is not configured, " +
325 "using current value of {}", matchIpv4Dscp);
326 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100327 matchIpv4Dscp = matchIpv4DscpEnabled;
328 log.info("Configured. Matching IPv4 DSCP and ECN is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800329 matchIpv4Dscp ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100330 }
Jian Lid9b5f552016-03-11 18:15:31 -0800331
332 Boolean matchIpv6AddressEnabled =
333 Tools.isPropertyEnabled(properties, "matchIpv6Address");
334 if (matchIpv6AddressEnabled == null) {
335 log.info("Matching IPv6 Address is not configured, " +
336 "using current value of {}", matchIpv6Address);
337 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100338 matchIpv6Address = matchIpv6AddressEnabled;
339 log.info("Configured. Matching IPv6 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800340 matchIpv6Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100341 }
Jian Lid9b5f552016-03-11 18:15:31 -0800342
343 Boolean matchIpv6FlowLabelEnabled =
344 Tools.isPropertyEnabled(properties, "matchIpv6FlowLabel");
345 if (matchIpv6FlowLabelEnabled == null) {
346 log.info("Matching IPv6 FlowLabel is not configured, " +
347 "using current value of {}", matchIpv6FlowLabel);
348 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100349 matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
350 log.info("Configured. Matching IPv6 FlowLabel is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800351 matchIpv6FlowLabel ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100352 }
Jian Lid9b5f552016-03-11 18:15:31 -0800353
354 Boolean matchTcpUdpPortsEnabled =
355 Tools.isPropertyEnabled(properties, "matchTcpUdpPorts");
356 if (matchTcpUdpPortsEnabled == null) {
357 log.info("Matching TCP/UDP fields is not configured, " +
358 "using current value of {}", matchTcpUdpPorts);
359 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100360 matchTcpUdpPorts = matchTcpUdpPortsEnabled;
361 log.info("Configured. Matching TCP/UDP fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800362 matchTcpUdpPorts ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100363 }
Jian Lid9b5f552016-03-11 18:15:31 -0800364
365 Boolean matchIcmpFieldsEnabled =
366 Tools.isPropertyEnabled(properties, "matchIcmpFields");
367 if (matchIcmpFieldsEnabled == null) {
368 log.info("Matching ICMP (v4 and v6) fields is not configured, " +
369 "using current value of {}", matchIcmpFields);
370 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100371 matchIcmpFields = matchIcmpFieldsEnabled;
372 log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800373 matchIcmpFields ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100374 }
Rusty Eddy29acad62015-07-07 19:33:47 -0700375
Jian Lid9b5f552016-03-11 18:15:31 -0800376 Boolean ignoreIpv4McastPacketsEnabled =
377 Tools.isPropertyEnabled(properties, "ignoreIpv4McastPackets");
378 if (ignoreIpv4McastPacketsEnabled == null) {
379 log.info("Ignore IPv4 multi-cast packet is not configured, " +
380 "using current value of {}", ignoreIpv4McastPackets);
381 } else {
Rusty Eddy29acad62015-07-07 19:33:47 -0700382 ignoreIpv4McastPackets = ignoreIpv4McastPacketsEnabled;
383 log.info("Configured. Ignore IPv4 multicast packets is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800384 ignoreIpv4McastPackets ? "enabled" : "disabled");
Rusty Eddy29acad62015-07-07 19:33:47 -0700385 }
Jian Lid9b5f552016-03-11 18:15:31 -0800386 flowTimeout = Tools.getIntegerProperty(properties, "flowTimeout", DEFAULT_TIMEOUT);
Yoonseon Han2f011ad2016-08-08 17:06:56 -0700387 log.info("Configured. Flow Timeout is configured to {} seconds", flowTimeout);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100388
Jian Lid9b5f552016-03-11 18:15:31 -0800389 flowPriority = Tools.getIntegerProperty(properties, "flowPriority", DEFAULT_PRIORITY);
390 log.info("Configured. Flow Priority is configured to {}", flowPriority);
tomc16656f2014-10-15 18:30:31 -0700391 }
tomc370ebd2014-09-16 01:25:21 -0700392
393 /**
394 * Packet processor responsible for forwarding packets along their paths.
395 */
396 private class ReactivePacketProcessor implements PacketProcessor {
397
398 @Override
399 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700400 // Stop processing if the packet has been handled, since we
401 // can't do any more to it.
Sahil Lelea69534f2015-07-16 15:14:57 -0700402
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800403 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700404 return;
405 }
tomdc95b8a2014-09-17 08:07:26 -0700406
tomc370ebd2014-09-16 01:25:21 -0700407 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700408 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700409
Jonathan Harte8600eb2015-01-12 10:30:45 -0800410 if (ethPkt == null) {
411 return;
412 }
413
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900414 // Bail if this is deemed to be a control packet.
415 if (isControlPacket(ethPkt)) {
416 return;
417 }
418
419 // Skip IPv6 multicast packet when IPv6 forward is disabled.
420 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800421 return;
422 }
423
tom642b2262014-09-17 13:52:55 -0700424 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700425
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700426 // Do not process link-local addresses in any way.
427 if (id.mac().isLinkLocal()) {
428 return;
429 }
430
Rusty Eddy29acad62015-07-07 19:33:47 -0700431 // Do not process IPv4 multicast packets, let mfwd handle them
432 if (ignoreIpv4McastPackets && ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
433 if (id.mac().isMulticast()) {
434 return;
435 }
436 }
437
tomc370ebd2014-09-16 01:25:21 -0700438 // Do we know who this is for? If not, flood and bail.
439 Host dst = hostService.getHost(id);
440 if (dst == null) {
441 flood(context);
442 return;
443 }
444
445 // Are we on an edge switch that our destination is on? If so,
446 // simply forward out to the destination and bail.
447 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700448 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
449 installRule(context, dst.location().port());
450 }
tomc370ebd2014-09-16 01:25:21 -0700451 return;
452 }
453
454 // Otherwise, get a set of paths that lead from here to the
455 // destination edge switch.
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100456 Set<Path> paths =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700457 topologyService.getPaths(topologyService.currentTopology(),
458 pkt.receivedFrom().deviceId(),
459 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700460 if (paths.isEmpty()) {
461 // If there are no paths, flood and bail.
462 flood(context);
463 return;
464 }
465
466 // Otherwise, pick a path that does not lead back to where we
467 // came from; if no such path, flood and bail.
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700468 Path path = pickForwardPathIfPossible(paths, pkt.receivedFrom().port());
tomc370ebd2014-09-16 01:25:21 -0700469 if (path == null) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700470 log.warn("Don't know where to go from here {} for {} -> {}",
471 pkt.receivedFrom(), ethPkt.getSourceMAC(), ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700472 flood(context);
473 return;
474 }
475
476 // Otherwise forward and be done with it.
alshabib7b2748f2014-09-16 20:21:11 -0700477 installRule(context, path.src().port());
tomc370ebd2014-09-16 01:25:21 -0700478 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800479
480 }
481
482 // Indicates whether this is a control packet, e.g. LLDP, BDDP
483 private boolean isControlPacket(Ethernet eth) {
484 short type = eth.getEtherType();
485 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700486 }
487
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800488 // Indicated whether this is an IPv6 multicast packet.
489 private boolean isIpv6Multicast(Ethernet eth) {
490 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
491 }
492
tomc370ebd2014-09-16 01:25:21 -0700493 // Selects a path from the given set that does not lead back to the
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700494 // specified port if possible.
495 private Path pickForwardPathIfPossible(Set<Path> paths, PortNumber notToPort) {
496 Path lastPath = null;
tomc370ebd2014-09-16 01:25:21 -0700497 for (Path path : paths) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700498 lastPath = path;
tomc370ebd2014-09-16 01:25:21 -0700499 if (!path.src().port().equals(notToPort)) {
500 return path;
501 }
502 }
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700503 return lastPath;
tomc370ebd2014-09-16 01:25:21 -0700504 }
505
tom642b2262014-09-17 13:52:55 -0700506 // Floods the specified packet if permissible.
tomc370ebd2014-09-16 01:25:21 -0700507 private void flood(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700508 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700509 context.inPacket().receivedFrom())) {
tom642b2262014-09-17 13:52:55 -0700510 packetOut(context, PortNumber.FLOOD);
tomc370ebd2014-09-16 01:25:21 -0700511 } else {
512 context.block();
513 }
514 }
515
tom642b2262014-09-17 13:52:55 -0700516 // Sends a packet out the specified port.
517 private void packetOut(PacketContext context, PortNumber portNumber) {
alshabib010c31d2014-09-26 10:01:12 -0700518 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700519 context.send();
520 }
521
522 // Install a rule forwarding the packet to the specified port.
523 private void installRule(PacketContext context, PortNumber portNumber) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100524 //
525 // We don't support (yet) buffer IDs in the Flow Service so
526 // packet out first.
527 //
528 Ethernet inPkt = context.inPacket().parsed();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700529 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100530
531 // If PacketOutOnly or ARP packet than forward directly to output port
532 if (packetOutOnly || inPkt.getEtherType() == Ethernet.TYPE_ARP) {
533 packetOut(context, portNumber);
534 return;
535 }
536
537 //
538 // If matchDstMacOnly
539 // Create flows matching dstMac only
540 // Else
541 // Create flows with default matching and include configured fields
542 //
543 if (matchDstMacOnly) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700544 selectorBuilder.matchEthDst(inPkt.getDestinationMAC());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100545 } else {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700546 selectorBuilder.matchInPort(context.inPacket().receivedFrom().port())
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800547 .matchEthSrc(inPkt.getSourceMAC())
Jonathan Hart430223a2015-04-22 17:39:02 -0700548 .matchEthDst(inPkt.getDestinationMAC());
alshabib7b2748f2014-09-16 20:21:11 -0700549
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100550 // If configured Match Vlan ID
551 if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700552 selectorBuilder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100553 }
alshabib7b2748f2014-09-16 20:21:11 -0700554
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100555 //
556 // If configured and EtherType is IPv4 - Match IPv4 and
557 // TCP/UDP/ICMP fields
558 //
559 if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
560 IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
561 byte ipv4Protocol = ipv4Packet.getProtocol();
562 Ip4Prefix matchIp4SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700563 Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
564 Ip4Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100565 Ip4Prefix matchIp4DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700566 Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
567 Ip4Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800568 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
Jonathan Hart430223a2015-04-22 17:39:02 -0700569 .matchIPSrc(matchIp4SrcPrefix)
570 .matchIPDst(matchIp4DstPrefix);
alshabib6eb438a2014-10-01 16:39:37 -0700571
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100572 if (matchIpv4Dscp) {
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -0800573 byte dscp = ipv4Packet.getDscp();
574 byte ecn = ipv4Packet.getEcn();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700575 selectorBuilder.matchIPDscp(dscp).matchIPEcn(ecn);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100576 }
577
578 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
579 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700580 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700581 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
582 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100583 }
584 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
585 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700586 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700587 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
588 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100589 }
590 if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
591 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700592 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700593 .matchIcmpType(icmpPacket.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100594 .matchIcmpCode(icmpPacket.getIcmpCode());
595 }
596 }
597
598 //
599 // If configured and EtherType is IPv6 - Match IPv6 and
600 // TCP/UDP/ICMP fields
601 //
602 if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
603 IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
604 byte ipv6NextHeader = ipv6Packet.getNextHeader();
605 Ip6Prefix matchIp6SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700606 Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
607 Ip6Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100608 Ip6Prefix matchIp6DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700609 Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
610 Ip6Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800611 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6)
612 .matchIPv6Src(matchIp6SrcPrefix)
Jonathan Hart430223a2015-04-22 17:39:02 -0700613 .matchIPv6Dst(matchIp6DstPrefix);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100614
615 if (matchIpv6FlowLabel) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700616 selectorBuilder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100617 }
618
619 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
620 TCP tcpPacket = (TCP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700621 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700622 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
623 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100624 }
625 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
626 UDP udpPacket = (UDP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700627 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700628 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
629 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100630 }
631 if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
632 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700633 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700634 .matchIcmpv6Type(icmp6Packet.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100635 .matchIcmpv6Code(icmp6Packet.getIcmpCode());
636 }
637 }
638 }
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700639 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
640 .setOutput(portNumber)
641 .build();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100642
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700643 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
644 .withSelector(selectorBuilder.build())
645 .withTreatment(treatment)
646 .withPriority(flowPriority)
647 .withFlag(ForwardingObjective.Flag.VERSATILE)
648 .fromApp(appId)
649 .makeTemporary(flowTimeout)
650 .add();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100651
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700652 flowObjectiveService.forward(context.inPacket().receivedFrom().deviceId(),
653 forwardingObjective);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100654
655 //
656 // If packetOutOfppTable
657 // Send packet back to the OpenFlow pipeline to match installed flow
658 // Else
659 // Send packet direction on the appropriate port
660 //
661 if (packetOutOfppTable) {
662 packetOut(context, PortNumber.TABLE);
663 } else {
664 packetOut(context, portNumber);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800665 }
tomc370ebd2014-09-16 01:25:21 -0700666 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700667
668 private class InternalTopologyListener implements TopologyListener {
669 @Override
670 public void event(TopologyEvent event) {
671 List<Event> reasons = event.reasons();
672 if (reasons != null) {
673 reasons.forEach(re -> {
674 if (re instanceof LinkEvent) {
675 LinkEvent le = (LinkEvent) re;
676 if (le.type() == LinkEvent.Type.LINK_REMOVED) {
677 fixBlackhole(le.subject().src());
678 }
679 }
680 });
681 }
682 }
683 }
684
685 private void fixBlackhole(ConnectPoint egress) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700686 Set<FlowEntry> rules = getFlowRulesFrom(egress);
Sahil Lelea69534f2015-07-16 15:14:57 -0700687 Set<SrcDstPair> pairs = findSrcDstPairs(rules);
688
Sahil Lele7b961662015-07-20 15:06:04 -0700689 Map<DeviceId, Set<Path>> srcPaths = new HashMap<>();
690
Thomas Vachuska8d033672015-07-21 16:15:04 -0700691 for (SrcDstPair sd : pairs) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700692 // get the edge deviceID for the src host
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700693 Host srcHost = hostService.getHost(HostId.hostId(sd.src));
694 Host dstHost = hostService.getHost(HostId.hostId(sd.dst));
695 if (srcHost != null && dstHost != null) {
696 DeviceId srcId = srcHost.location().deviceId();
697 DeviceId dstId = dstHost.location().deviceId();
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800698 log.trace("SRC ID is {}, DST ID is {}", srcId, dstId);
Sahil Lelea69534f2015-07-16 15:14:57 -0700699
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700700 cleanFlowRules(sd, egress.deviceId());
Sahil Lelea69534f2015-07-16 15:14:57 -0700701
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700702 Set<Path> shortestPaths = srcPaths.get(srcId);
703 if (shortestPaths == null) {
704 shortestPaths = topologyService.getPaths(topologyService.currentTopology(),
705 egress.deviceId(), srcId);
706 srcPaths.put(srcId, shortestPaths);
707 }
708 backTrackBadNodes(shortestPaths, dstId, sd);
Sahil Lele7b961662015-07-20 15:06:04 -0700709 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700710 }
711 }
712
713 // Backtracks from link down event to remove flows that lead to blackhole
714 private void backTrackBadNodes(Set<Path> shortestPaths, DeviceId dstId, SrcDstPair sd) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700715 for (Path p : shortestPaths) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700716 List<Link> pathLinks = p.links();
717 for (int i = 0; i < pathLinks.size(); i = i + 1) {
718 Link curLink = pathLinks.get(i);
719 DeviceId curDevice = curLink.src().deviceId();
Sahil Lelea69534f2015-07-16 15:14:57 -0700720
721 // skipping the first link because this link's src has already been pruned beforehand
722 if (i != 0) {
723 cleanFlowRules(sd, curDevice);
724 }
725
Thomas Vachuska8d033672015-07-21 16:15:04 -0700726 Set<Path> pathsFromCurDevice =
727 topologyService.getPaths(topologyService.currentTopology(),
728 curDevice, dstId);
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700729 if (pickForwardPathIfPossible(pathsFromCurDevice, curLink.src().port()) != null) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700730 break;
731 } else {
732 if (i + 1 == pathLinks.size()) {
733 cleanFlowRules(sd, curLink.dst().deviceId());
734 }
735 }
736 }
737 }
738 }
739
740 // Removes flow rules off specified device with specific SrcDstPair
741 private void cleanFlowRules(SrcDstPair pair, DeviceId id) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800742 log.trace("Searching for flow rules to remove from: {}", id);
743 log.trace("Removing flows w/ SRC={}, DST={}", pair.src, pair.dst);
Sahil Lelea69534f2015-07-16 15:14:57 -0700744 for (FlowEntry r : flowRuleService.getFlowEntries(id)) {
745 boolean matchesSrc = false, matchesDst = false;
746 for (Instruction i : r.treatment().allInstructions()) {
747 if (i.type() == Instruction.Type.OUTPUT) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700748 // if the flow has matching src and dst
Sahil Lelea69534f2015-07-16 15:14:57 -0700749 for (Criterion cr : r.selector().criteria()) {
750 if (cr.type() == Criterion.Type.ETH_DST) {
751 if (((EthCriterion) cr).mac().equals(pair.dst)) {
752 matchesDst = true;
753 }
754 } else if (cr.type() == Criterion.Type.ETH_SRC) {
755 if (((EthCriterion) cr).mac().equals(pair.src)) {
756 matchesSrc = true;
757 }
758 }
759 }
760 }
761 }
762 if (matchesDst && matchesSrc) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800763 log.trace("Removed flow rule from device: {}", id);
Sahil Lelea69534f2015-07-16 15:14:57 -0700764 flowRuleService.removeFlowRules((FlowRule) r);
765 }
766 }
767
768 }
769
770 // Returns a set of src/dst MAC pairs extracted from the specified set of flow entries
771 private Set<SrcDstPair> findSrcDstPairs(Set<FlowEntry> rules) {
772 ImmutableSet.Builder<SrcDstPair> builder = ImmutableSet.builder();
Thomas Vachuska8d033672015-07-21 16:15:04 -0700773 for (FlowEntry r : rules) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700774 MacAddress src = null, dst = null;
Thomas Vachuska8d033672015-07-21 16:15:04 -0700775 for (Criterion cr : r.selector().criteria()) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700776 if (cr.type() == Criterion.Type.ETH_DST) {
777 dst = ((EthCriterion) cr).mac();
778 } else if (cr.type() == Criterion.Type.ETH_SRC) {
779 src = ((EthCriterion) cr).mac();
780 }
781 }
782 builder.add(new SrcDstPair(src, dst));
783 }
784 return builder.build();
785 }
786
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700787 // Returns set of flow entries which were created by this application and
788 // which egress from the specified connection port
Sahil Lelea69534f2015-07-16 15:14:57 -0700789 private Set<FlowEntry> getFlowRulesFrom(ConnectPoint egress) {
790 ImmutableSet.Builder<FlowEntry> builder = ImmutableSet.builder();
791 flowRuleService.getFlowEntries(egress.deviceId()).forEach(r -> {
792 if (r.appId() == appId.id()) {
793 r.treatment().allInstructions().forEach(i -> {
794 if (i.type() == Instruction.Type.OUTPUT) {
795 if (((Instructions.OutputInstruction) i).port().equals(egress.port())) {
796 builder.add(r);
797 }
798 }
799 });
800 }
801 });
802
803 return builder.build();
804 }
805
806 // Wrapper class for a source and destination pair of MAC addresses
807 private final class SrcDstPair {
808 final MacAddress src;
809 final MacAddress dst;
810
811 private SrcDstPair(MacAddress src, MacAddress dst) {
812 this.src = src;
813 this.dst = dst;
814 }
815
816 @Override
817 public boolean equals(Object o) {
818 if (this == o) {
819 return true;
820 }
821 if (o == null || getClass() != o.getClass()) {
822 return false;
823 }
824 SrcDstPair that = (SrcDstPair) o;
825 return Objects.equals(src, that.src) &&
826 Objects.equals(dst, that.dst);
827 }
828
829 @Override
830 public int hashCode() {
831 return Objects.hash(src, dst);
832 }
833 }
alshabib030111e2014-09-15 15:56:42 -0700834}