blob: 6653c63585782180a382f63575528742002313cc [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;
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
84import static com.google.common.base.Strings.isNullOrEmpty;
85import static org.slf4j.LoggerFactory.getLogger;
86
tomc370ebd2014-09-16 01:25:21 -070087/**
88 * Sample reactive forwarding application.
89 */
90@Component(immediate = true)
alshabib030111e2014-09-15 15:56:42 -070091public class ReactiveForwarding {
92
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010093 private static final int DEFAULT_TIMEOUT = 10;
94 private static final int DEFAULT_PRIORITY = 10;
alshabibba5ac482014-10-02 17:15:20 -070095
tomc370ebd2014-09-16 01:25:21 -070096 private final Logger log = getLogger(getClass());
97
alshabib030111e2014-09-15 15:56:42 -070098 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected TopologyService topologyService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected PacketService packetService;
103
alshabib8aef1ad2014-09-15 17:47:31 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected HostService hostService;
106
alshabib7b2748f2014-09-16 20:21:11 -0700107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected FlowRuleService flowRuleService;
109
alshabib92c65ad2014-10-08 21:56:05 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700111 protected FlowObjectiveService flowObjectiveService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib92c65ad2014-10-08 21:56:05 -0700114 protected CoreService coreService;
115
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected ComponentConfigService cfgService;
118
tomc370ebd2014-09-16 01:25:21 -0700119 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -0700120
alshabiba68eb962014-09-24 20:34:13 -0700121 private ApplicationId appId;
122
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800123 @Property(name = "packetOutOnly", boolValue = false,
124 label = "Enable packet-out only forwarding; default is false")
125 private boolean packetOutOnly = false;
tomc16656f2014-10-15 18:30:31 -0700126
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100127 @Property(name = "packetOutOfppTable", boolValue = false,
128 label = "Enable first packet forwarding using OFPP_TABLE port " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700129 "instead of PacketOut with actual port; default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100130 private boolean packetOutOfppTable = false;
131
132 @Property(name = "flowTimeout", intValue = DEFAULT_TIMEOUT,
133 label = "Configure Flow Timeout for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700134 "default is 10 sec")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100135 private int flowTimeout = DEFAULT_TIMEOUT;
136
137 @Property(name = "flowPriority", intValue = DEFAULT_PRIORITY,
138 label = "Configure Flow Priority for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700139 "default is 10")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100140 private int flowPriority = DEFAULT_PRIORITY;
141
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900142 @Property(name = "ipv6Forwarding", boolValue = false,
143 label = "Enable IPv6 forwarding; default is false")
144 private boolean ipv6Forwarding = false;
145
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100146 @Property(name = "matchDstMacOnly", boolValue = false,
147 label = "Enable matching Dst Mac Only; default is false")
148 private boolean matchDstMacOnly = false;
149
150 @Property(name = "matchVlanId", boolValue = false,
151 label = "Enable matching Vlan ID; default is false")
152 private boolean matchVlanId = false;
153
154 @Property(name = "matchIpv4Address", boolValue = false,
155 label = "Enable matching IPv4 Addresses; default is false")
156 private boolean matchIpv4Address = false;
157
158 @Property(name = "matchIpv4Dscp", boolValue = false,
159 label = "Enable matching IPv4 DSCP and ECN; default is false")
160 private boolean matchIpv4Dscp = false;
161
162 @Property(name = "matchIpv6Address", boolValue = false,
163 label = "Enable matching IPv6 Addresses; default is false")
164 private boolean matchIpv6Address = false;
165
166 @Property(name = "matchIpv6FlowLabel", boolValue = false,
167 label = "Enable matching IPv6 FlowLabel; default is false")
168 private boolean matchIpv6FlowLabel = false;
169
170 @Property(name = "matchTcpUdpPorts", boolValue = false,
171 label = "Enable matching TCP/UDP ports; default is false")
172 private boolean matchTcpUdpPorts = false;
173
174 @Property(name = "matchIcmpFields", boolValue = false,
175 label = "Enable matching ICMPv4 and ICMPv6 fields; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700176 "default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100177 private boolean matchIcmpFields = false;
178
Sahil Lelea69534f2015-07-16 15:14:57 -0700179
Rusty Eddy29acad62015-07-07 19:33:47 -0700180 @Property(name = "ignoreIPv4Multicast", boolValue = false,
181 label = "Ignore (do not forward) IPv4 multicast packets; default is false")
182 private boolean ignoreIpv4McastPackets = false;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100183
Sahil Lelea69534f2015-07-16 15:14:57 -0700184 private final TopologyListener topologyListener = new InternalTopologyListener();
185
186
alshabib030111e2014-09-15 15:56:42 -0700187 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900188 public void activate(ComponentContext context) {
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700189 cfgService.registerProperties(getClass());
Brian O'Connorabafb502014-12-02 22:26:20 -0800190 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800191
Brian O'Connor3b783262015-07-29 17:49:24 -0700192 packetService.addProcessor(processor, PacketProcessor.director(2));
Sahil Lelea69534f2015-07-16 15:14:57 -0700193 topologyService.addListener(topologyListener);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900194 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700195 requestIntercepts();
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800196
Thomas Vachuska8d033672015-07-21 16:15:04 -0700197 log.info("Started", appId.id());
Charles M.C. Chane148de82015-05-06 12:38:21 +0800198 }
199
200 @Deactivate
201 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800202 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700203 withdrawIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800204 flowRuleService.removeFlowRulesById(appId);
205 packetService.removeProcessor(processor);
Sahil Lelea69534f2015-07-16 15:14:57 -0700206 topologyService.removeListener(topologyListener);
Charles M.C. Chane148de82015-05-06 12:38:21 +0800207 processor = null;
208 log.info("Stopped");
209 }
210
211 @Modified
212 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800213 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700214 requestIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800215 }
216
217 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700218 * Request packet in via packet service.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800219 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700220 private void requestIntercepts() {
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800221 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
222 selector.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700223 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100224 selector.matchEthType(Ethernet.TYPE_ARP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700225 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100226
Thomas Vachuska27bee092015-06-23 19:03:10 -0700227 selector.matchEthType(Ethernet.TYPE_IPV6);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100228 if (ipv6Forwarding) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700229 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
230 } else {
231 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100232 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900233 }
234
235 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700236 * Cancel request for packet in via packet service.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700237 */
238 private void withdrawIntercepts() {
239 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
240 selector.matchEthType(Ethernet.TYPE_IPV4);
241 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
242 selector.matchEthType(Ethernet.TYPE_ARP);
243 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
244 selector.matchEthType(Ethernet.TYPE_IPV6);
245 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
246 }
247
248 /**
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900249 * Extracts properties from the component configuration context.
250 *
251 * @param context the component context
252 */
253 private void readComponentConfiguration(ComponentContext context) {
254 Dictionary<?, ?> properties = context.getProperties();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100255 boolean packetOutOnlyEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700256 isPropertyEnabled(properties, "packetOutOnly");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900257 if (packetOutOnly != packetOutOnlyEnabled) {
258 packetOutOnly = packetOutOnlyEnabled;
259 log.info("Configured. Packet-out only forwarding is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700260 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700261 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100262 boolean packetOutOfppTableEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700263 isPropertyEnabled(properties, "packetOutOfppTable");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100264 if (packetOutOfppTable != packetOutOfppTableEnabled) {
265 packetOutOfppTable = packetOutOfppTableEnabled;
266 log.info("Configured. Forwarding using OFPP_TABLE port is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700267 packetOutOfppTable ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100268 }
269 boolean ipv6ForwardingEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700270 isPropertyEnabled(properties, "ipv6Forwarding");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900271 if (ipv6Forwarding != ipv6ForwardingEnabled) {
272 ipv6Forwarding = ipv6ForwardingEnabled;
273 log.info("Configured. IPv6 forwarding is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700274 ipv6Forwarding ? "enabled" : "disabled");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900275 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100276 boolean matchDstMacOnlyEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700277 isPropertyEnabled(properties, "matchDstMacOnly");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100278 if (matchDstMacOnly != matchDstMacOnlyEnabled) {
279 matchDstMacOnly = matchDstMacOnlyEnabled;
280 log.info("Configured. Match Dst MAC Only is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700281 matchDstMacOnly ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100282 }
283 boolean matchVlanIdEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700284 isPropertyEnabled(properties, "matchVlanId");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100285 if (matchVlanId != matchVlanIdEnabled) {
286 matchVlanId = matchVlanIdEnabled;
287 log.info("Configured. Matching Vlan ID is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700288 matchVlanId ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100289 }
290 boolean matchIpv4AddressEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700291 isPropertyEnabled(properties, "matchIpv4Address");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100292 if (matchIpv4Address != matchIpv4AddressEnabled) {
293 matchIpv4Address = matchIpv4AddressEnabled;
294 log.info("Configured. Matching IPv4 Addresses is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700295 matchIpv4Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100296 }
297 boolean matchIpv4DscpEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700298 isPropertyEnabled(properties, "matchIpv4Dscp");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100299 if (matchIpv4Dscp != matchIpv4DscpEnabled) {
300 matchIpv4Dscp = matchIpv4DscpEnabled;
301 log.info("Configured. Matching IPv4 DSCP and ECN is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700302 matchIpv4Dscp ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100303 }
304 boolean matchIpv6AddressEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700305 isPropertyEnabled(properties, "matchIpv6Address");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100306 if (matchIpv6Address != matchIpv6AddressEnabled) {
307 matchIpv6Address = matchIpv6AddressEnabled;
308 log.info("Configured. Matching IPv6 Addresses is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700309 matchIpv6Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100310 }
311 boolean matchIpv6FlowLabelEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700312 isPropertyEnabled(properties, "matchIpv6FlowLabel");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100313 if (matchIpv6FlowLabel != matchIpv6FlowLabelEnabled) {
314 matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
315 log.info("Configured. Matching IPv6 FlowLabel is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700316 matchIpv6FlowLabel ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100317 }
318 boolean matchTcpUdpPortsEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700319 isPropertyEnabled(properties, "matchTcpUdpPorts");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100320 if (matchTcpUdpPorts != matchTcpUdpPortsEnabled) {
321 matchTcpUdpPorts = matchTcpUdpPortsEnabled;
322 log.info("Configured. Matching TCP/UDP fields is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700323 matchTcpUdpPorts ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100324 }
325 boolean matchIcmpFieldsEnabled =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700326 isPropertyEnabled(properties, "matchIcmpFields");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100327 if (matchIcmpFields != matchIcmpFieldsEnabled) {
328 matchIcmpFields = matchIcmpFieldsEnabled;
329 log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
Thomas Vachuska27bee092015-06-23 19:03:10 -0700330 matchIcmpFields ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100331 }
332 Integer flowTimeoutConfigured =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700333 getIntegerProperty(properties, "flowTimeout");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100334 if (flowTimeoutConfigured == null) {
Antonio Marsicob8b8d752015-12-17 16:28:34 +0100335 flowTimeout = DEFAULT_TIMEOUT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100336 log.info("Flow Timeout is not configured, default value is {}",
337 flowTimeout);
338 } else {
339 flowTimeout = flowTimeoutConfigured;
340 log.info("Configured. Flow Timeout is configured to {}",
341 flowTimeout, " seconds");
342 }
343 Integer flowPriorityConfigured =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700344 getIntegerProperty(properties, "flowPriority");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100345 if (flowPriorityConfigured == null) {
Antonio Marsicob8b8d752015-12-17 16:28:34 +0100346 flowPriority = DEFAULT_PRIORITY;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100347 log.info("Flow Priority is not configured, default value is {}",
348 flowPriority);
349 } else {
350 flowPriority = flowPriorityConfigured;
351 log.info("Configured. Flow Priority is configured to {}",
352 flowPriority);
353 }
Rusty Eddy29acad62015-07-07 19:33:47 -0700354
355 boolean ignoreIpv4McastPacketsEnabled =
356 isPropertyEnabled(properties, "ignoreIpv4McastPackets");
357 if (ignoreIpv4McastPackets != ignoreIpv4McastPacketsEnabled) {
358 ignoreIpv4McastPackets = ignoreIpv4McastPacketsEnabled;
359 log.info("Configured. Ignore IPv4 multicast packets is {}",
Thomas Vachuska8d033672015-07-21 16:15:04 -0700360 ignoreIpv4McastPackets ? "enabled" : "disabled");
Rusty Eddy29acad62015-07-07 19:33:47 -0700361 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100362 }
363
364 /**
365 * Get Integer property from the propertyName
366 * Return null if propertyName is not found.
367 *
Thomas Vachuska27bee092015-06-23 19:03:10 -0700368 * @param properties properties to be looked up
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100369 * @param propertyName the name of the property to look up
370 * @return value when the propertyName is defined or return null
371 */
372 private static Integer getIntegerProperty(Dictionary<?, ?> properties,
373 String propertyName) {
374 Integer value = null;
375 try {
maojianwei8bf77b72016-02-15 15:18:24 +0800376 String s = Tools.get(properties, propertyName);
377 value = isNullOrEmpty(s) ? value : Integer.parseInt(s);
Pavlin Radoslavovb9e50df2015-02-20 20:01:26 -0800378 } catch (NumberFormatException | ClassCastException e) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100379 value = null;
380 }
381 return value;
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900382 }
383
384 /**
385 * Check property name is defined and set to true.
386 *
Thomas Vachuska27bee092015-06-23 19:03:10 -0700387 * @param properties properties to be looked up
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900388 * @param propertyName the name of the property to look up
389 * @return true when the propertyName is defined and set to true
390 */
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100391 private static boolean isPropertyEnabled(Dictionary<?, ?> properties,
392 String propertyName) {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900393 boolean enabled = false;
394 try {
maojianwei8bf77b72016-02-15 15:18:24 +0800395 String flag = Tools.get(properties, propertyName);
396 enabled = isNullOrEmpty(flag) ? enabled : flag.equals("true");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900397 } catch (ClassCastException e) {
398 // No propertyName defined.
399 enabled = false;
400 }
401 return enabled;
tomc16656f2014-10-15 18:30:31 -0700402 }
tomc370ebd2014-09-16 01:25:21 -0700403
404 /**
405 * Packet processor responsible for forwarding packets along their paths.
406 */
407 private class ReactivePacketProcessor implements PacketProcessor {
408
409 @Override
410 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700411 // Stop processing if the packet has been handled, since we
412 // can't do any more to it.
Sahil Lelea69534f2015-07-16 15:14:57 -0700413
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800414 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700415 return;
416 }
tomdc95b8a2014-09-17 08:07:26 -0700417
tomc370ebd2014-09-16 01:25:21 -0700418 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700419 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700420
Jonathan Harte8600eb2015-01-12 10:30:45 -0800421 if (ethPkt == null) {
422 return;
423 }
424
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900425 // Bail if this is deemed to be a control packet.
426 if (isControlPacket(ethPkt)) {
427 return;
428 }
429
430 // Skip IPv6 multicast packet when IPv6 forward is disabled.
431 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800432 return;
433 }
434
tom642b2262014-09-17 13:52:55 -0700435 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700436
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700437 // Do not process link-local addresses in any way.
438 if (id.mac().isLinkLocal()) {
439 return;
440 }
441
Rusty Eddy29acad62015-07-07 19:33:47 -0700442 // Do not process IPv4 multicast packets, let mfwd handle them
443 if (ignoreIpv4McastPackets && ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
444 if (id.mac().isMulticast()) {
445 return;
446 }
447 }
448
tomc370ebd2014-09-16 01:25:21 -0700449 // Do we know who this is for? If not, flood and bail.
450 Host dst = hostService.getHost(id);
451 if (dst == null) {
452 flood(context);
453 return;
454 }
455
456 // Are we on an edge switch that our destination is on? If so,
457 // simply forward out to the destination and bail.
458 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700459 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
460 installRule(context, dst.location().port());
461 }
tomc370ebd2014-09-16 01:25:21 -0700462 return;
463 }
464
465 // Otherwise, get a set of paths that lead from here to the
466 // destination edge switch.
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100467 Set<Path> paths =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700468 topologyService.getPaths(topologyService.currentTopology(),
469 pkt.receivedFrom().deviceId(),
470 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700471 if (paths.isEmpty()) {
472 // If there are no paths, flood and bail.
473 flood(context);
474 return;
475 }
476
477 // Otherwise, pick a path that does not lead back to where we
478 // came from; if no such path, flood and bail.
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700479 Path path = pickForwardPathIfPossible(paths, pkt.receivedFrom().port());
tomc370ebd2014-09-16 01:25:21 -0700480 if (path == null) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700481 log.warn("Don't know where to go from here {} for {} -> {}",
482 pkt.receivedFrom(), ethPkt.getSourceMAC(), ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700483 flood(context);
484 return;
485 }
486
487 // Otherwise forward and be done with it.
alshabib7b2748f2014-09-16 20:21:11 -0700488 installRule(context, path.src().port());
tomc370ebd2014-09-16 01:25:21 -0700489 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800490
491 }
492
493 // Indicates whether this is a control packet, e.g. LLDP, BDDP
494 private boolean isControlPacket(Ethernet eth) {
495 short type = eth.getEtherType();
496 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700497 }
498
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800499 // Indicated whether this is an IPv6 multicast packet.
500 private boolean isIpv6Multicast(Ethernet eth) {
501 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
502 }
503
tomc370ebd2014-09-16 01:25:21 -0700504 // Selects a path from the given set that does not lead back to the
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700505 // specified port if possible.
506 private Path pickForwardPathIfPossible(Set<Path> paths, PortNumber notToPort) {
507 Path lastPath = null;
tomc370ebd2014-09-16 01:25:21 -0700508 for (Path path : paths) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700509 lastPath = path;
tomc370ebd2014-09-16 01:25:21 -0700510 if (!path.src().port().equals(notToPort)) {
511 return path;
512 }
513 }
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700514 return lastPath;
tomc370ebd2014-09-16 01:25:21 -0700515 }
516
tom642b2262014-09-17 13:52:55 -0700517 // Floods the specified packet if permissible.
tomc370ebd2014-09-16 01:25:21 -0700518 private void flood(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700519 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700520 context.inPacket().receivedFrom())) {
tom642b2262014-09-17 13:52:55 -0700521 packetOut(context, PortNumber.FLOOD);
tomc370ebd2014-09-16 01:25:21 -0700522 } else {
523 context.block();
524 }
525 }
526
tom642b2262014-09-17 13:52:55 -0700527 // Sends a packet out the specified port.
528 private void packetOut(PacketContext context, PortNumber portNumber) {
alshabib010c31d2014-09-26 10:01:12 -0700529 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700530 context.send();
531 }
532
533 // Install a rule forwarding the packet to the specified port.
534 private void installRule(PacketContext context, PortNumber portNumber) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100535 //
536 // We don't support (yet) buffer IDs in the Flow Service so
537 // packet out first.
538 //
539 Ethernet inPkt = context.inPacket().parsed();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700540 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100541
542 // If PacketOutOnly or ARP packet than forward directly to output port
543 if (packetOutOnly || inPkt.getEtherType() == Ethernet.TYPE_ARP) {
544 packetOut(context, portNumber);
545 return;
546 }
547
548 //
549 // If matchDstMacOnly
550 // Create flows matching dstMac only
551 // Else
552 // Create flows with default matching and include configured fields
553 //
554 if (matchDstMacOnly) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700555 selectorBuilder.matchEthDst(inPkt.getDestinationMAC());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100556 } else {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700557 selectorBuilder.matchInPort(context.inPacket().receivedFrom().port())
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800558 .matchEthSrc(inPkt.getSourceMAC())
Jonathan Hart430223a2015-04-22 17:39:02 -0700559 .matchEthDst(inPkt.getDestinationMAC());
alshabib7b2748f2014-09-16 20:21:11 -0700560
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100561 // If configured Match Vlan ID
562 if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700563 selectorBuilder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100564 }
alshabib7b2748f2014-09-16 20:21:11 -0700565
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100566 //
567 // If configured and EtherType is IPv4 - Match IPv4 and
568 // TCP/UDP/ICMP fields
569 //
570 if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
571 IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
572 byte ipv4Protocol = ipv4Packet.getProtocol();
573 Ip4Prefix matchIp4SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700574 Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
575 Ip4Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100576 Ip4Prefix matchIp4DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700577 Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
578 Ip4Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800579 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
Jonathan Hart430223a2015-04-22 17:39:02 -0700580 .matchIPSrc(matchIp4SrcPrefix)
581 .matchIPDst(matchIp4DstPrefix);
alshabib6eb438a2014-10-01 16:39:37 -0700582
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100583 if (matchIpv4Dscp) {
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -0800584 byte dscp = ipv4Packet.getDscp();
585 byte ecn = ipv4Packet.getEcn();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700586 selectorBuilder.matchIPDscp(dscp).matchIPEcn(ecn);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100587 }
588
589 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
590 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700591 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700592 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
593 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100594 }
595 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
596 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700597 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700598 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
599 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100600 }
601 if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
602 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700603 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700604 .matchIcmpType(icmpPacket.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100605 .matchIcmpCode(icmpPacket.getIcmpCode());
606 }
607 }
608
609 //
610 // If configured and EtherType is IPv6 - Match IPv6 and
611 // TCP/UDP/ICMP fields
612 //
613 if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
614 IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
615 byte ipv6NextHeader = ipv6Packet.getNextHeader();
616 Ip6Prefix matchIp6SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700617 Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
618 Ip6Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100619 Ip6Prefix matchIp6DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700620 Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
621 Ip6Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800622 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6)
623 .matchIPv6Src(matchIp6SrcPrefix)
Jonathan Hart430223a2015-04-22 17:39:02 -0700624 .matchIPv6Dst(matchIp6DstPrefix);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100625
626 if (matchIpv6FlowLabel) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700627 selectorBuilder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100628 }
629
630 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
631 TCP tcpPacket = (TCP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700632 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700633 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
634 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100635 }
636 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
637 UDP udpPacket = (UDP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700638 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700639 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
640 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100641 }
642 if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
643 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700644 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700645 .matchIcmpv6Type(icmp6Packet.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100646 .matchIcmpv6Code(icmp6Packet.getIcmpCode());
647 }
648 }
649 }
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700650 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
651 .setOutput(portNumber)
652 .build();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100653
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700654 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
655 .withSelector(selectorBuilder.build())
656 .withTreatment(treatment)
657 .withPriority(flowPriority)
658 .withFlag(ForwardingObjective.Flag.VERSATILE)
659 .fromApp(appId)
660 .makeTemporary(flowTimeout)
661 .add();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100662
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700663 flowObjectiveService.forward(context.inPacket().receivedFrom().deviceId(),
664 forwardingObjective);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100665
666 //
667 // If packetOutOfppTable
668 // Send packet back to the OpenFlow pipeline to match installed flow
669 // Else
670 // Send packet direction on the appropriate port
671 //
672 if (packetOutOfppTable) {
673 packetOut(context, PortNumber.TABLE);
674 } else {
675 packetOut(context, portNumber);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800676 }
tomc370ebd2014-09-16 01:25:21 -0700677 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700678
679 private class InternalTopologyListener implements TopologyListener {
680 @Override
681 public void event(TopologyEvent event) {
682 List<Event> reasons = event.reasons();
683 if (reasons != null) {
684 reasons.forEach(re -> {
685 if (re instanceof LinkEvent) {
686 LinkEvent le = (LinkEvent) re;
687 if (le.type() == LinkEvent.Type.LINK_REMOVED) {
688 fixBlackhole(le.subject().src());
689 }
690 }
691 });
692 }
693 }
694 }
695
696 private void fixBlackhole(ConnectPoint egress) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700697 Set<FlowEntry> rules = getFlowRulesFrom(egress);
Sahil Lelea69534f2015-07-16 15:14:57 -0700698 Set<SrcDstPair> pairs = findSrcDstPairs(rules);
699
Sahil Lele7b961662015-07-20 15:06:04 -0700700 Map<DeviceId, Set<Path>> srcPaths = new HashMap<>();
701
Thomas Vachuska8d033672015-07-21 16:15:04 -0700702 for (SrcDstPair sd : pairs) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700703 // get the edge deviceID for the src host
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700704 Host srcHost = hostService.getHost(HostId.hostId(sd.src));
705 Host dstHost = hostService.getHost(HostId.hostId(sd.dst));
706 if (srcHost != null && dstHost != null) {
707 DeviceId srcId = srcHost.location().deviceId();
708 DeviceId dstId = dstHost.location().deviceId();
709 log.trace("SRC ID is " + srcId + ", DST ID is " + dstId);
Sahil Lelea69534f2015-07-16 15:14:57 -0700710
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700711 cleanFlowRules(sd, egress.deviceId());
Sahil Lelea69534f2015-07-16 15:14:57 -0700712
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700713 Set<Path> shortestPaths = srcPaths.get(srcId);
714 if (shortestPaths == null) {
715 shortestPaths = topologyService.getPaths(topologyService.currentTopology(),
716 egress.deviceId(), srcId);
717 srcPaths.put(srcId, shortestPaths);
718 }
719 backTrackBadNodes(shortestPaths, dstId, sd);
Sahil Lele7b961662015-07-20 15:06:04 -0700720 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700721 }
722 }
723
724 // Backtracks from link down event to remove flows that lead to blackhole
725 private void backTrackBadNodes(Set<Path> shortestPaths, DeviceId dstId, SrcDstPair sd) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700726 for (Path p : shortestPaths) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700727 List<Link> pathLinks = p.links();
728 for (int i = 0; i < pathLinks.size(); i = i + 1) {
729 Link curLink = pathLinks.get(i);
730 DeviceId curDevice = curLink.src().deviceId();
Sahil Lelea69534f2015-07-16 15:14:57 -0700731
732 // skipping the first link because this link's src has already been pruned beforehand
733 if (i != 0) {
734 cleanFlowRules(sd, curDevice);
735 }
736
Thomas Vachuska8d033672015-07-21 16:15:04 -0700737 Set<Path> pathsFromCurDevice =
738 topologyService.getPaths(topologyService.currentTopology(),
739 curDevice, dstId);
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700740 if (pickForwardPathIfPossible(pathsFromCurDevice, curLink.src().port()) != null) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700741 break;
742 } else {
743 if (i + 1 == pathLinks.size()) {
744 cleanFlowRules(sd, curLink.dst().deviceId());
745 }
746 }
747 }
748 }
749 }
750
751 // Removes flow rules off specified device with specific SrcDstPair
752 private void cleanFlowRules(SrcDstPair pair, DeviceId id) {
753 log.trace("Searching for flow rules to remove from: " + id);
754 log.trace("Removing flows w/ SRC=" + pair.src + ", DST=" + pair.dst);
755 for (FlowEntry r : flowRuleService.getFlowEntries(id)) {
756 boolean matchesSrc = false, matchesDst = false;
757 for (Instruction i : r.treatment().allInstructions()) {
758 if (i.type() == Instruction.Type.OUTPUT) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700759 // if the flow has matching src and dst
Sahil Lelea69534f2015-07-16 15:14:57 -0700760 for (Criterion cr : r.selector().criteria()) {
761 if (cr.type() == Criterion.Type.ETH_DST) {
762 if (((EthCriterion) cr).mac().equals(pair.dst)) {
763 matchesDst = true;
764 }
765 } else if (cr.type() == Criterion.Type.ETH_SRC) {
766 if (((EthCriterion) cr).mac().equals(pair.src)) {
767 matchesSrc = true;
768 }
769 }
770 }
771 }
772 }
773 if (matchesDst && matchesSrc) {
774 log.trace("Removed flow rule from device: " + id);
775 flowRuleService.removeFlowRules((FlowRule) r);
776 }
777 }
778
779 }
780
781 // Returns a set of src/dst MAC pairs extracted from the specified set of flow entries
782 private Set<SrcDstPair> findSrcDstPairs(Set<FlowEntry> rules) {
783 ImmutableSet.Builder<SrcDstPair> builder = ImmutableSet.builder();
Thomas Vachuska8d033672015-07-21 16:15:04 -0700784 for (FlowEntry r : rules) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700785 MacAddress src = null, dst = null;
Thomas Vachuska8d033672015-07-21 16:15:04 -0700786 for (Criterion cr : r.selector().criteria()) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700787 if (cr.type() == Criterion.Type.ETH_DST) {
788 dst = ((EthCriterion) cr).mac();
789 } else if (cr.type() == Criterion.Type.ETH_SRC) {
790 src = ((EthCriterion) cr).mac();
791 }
792 }
793 builder.add(new SrcDstPair(src, dst));
794 }
795 return builder.build();
796 }
797
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700798 // Returns set of flow entries which were created by this application and
799 // which egress from the specified connection port
Sahil Lelea69534f2015-07-16 15:14:57 -0700800 private Set<FlowEntry> getFlowRulesFrom(ConnectPoint egress) {
801 ImmutableSet.Builder<FlowEntry> builder = ImmutableSet.builder();
802 flowRuleService.getFlowEntries(egress.deviceId()).forEach(r -> {
803 if (r.appId() == appId.id()) {
804 r.treatment().allInstructions().forEach(i -> {
805 if (i.type() == Instruction.Type.OUTPUT) {
806 if (((Instructions.OutputInstruction) i).port().equals(egress.port())) {
807 builder.add(r);
808 }
809 }
810 });
811 }
812 });
813
814 return builder.build();
815 }
816
817 // Wrapper class for a source and destination pair of MAC addresses
818 private final class SrcDstPair {
819 final MacAddress src;
820 final MacAddress dst;
821
822 private SrcDstPair(MacAddress src, MacAddress dst) {
823 this.src = src;
824 this.dst = dst;
825 }
826
827 @Override
828 public boolean equals(Object o) {
829 if (this == o) {
830 return true;
831 }
832 if (o == null || getClass() != o.getClass()) {
833 return false;
834 }
835 SrcDstPair that = (SrcDstPair) o;
836 return Objects.equals(src, that.src) &&
837 Objects.equals(dst, that.dst);
838 }
839
840 @Override
841 public int hashCode() {
842 return Objects.hash(src, dst);
843 }
844 }
alshabib030111e2014-09-15 15:56:42 -0700845}