blob: 9917a851cd495a461c44c304394a7429faff6f19 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2014-present Open Networking Foundation
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;
Thomas Vachuskae9d21862017-10-03 14:05:48 -070026import org.apache.felix.scr.annotations.Service;
Jonathan Harte8600eb2015-01-12 10:30:45 -080027import org.onlab.packet.Ethernet;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010028import org.onlab.packet.ICMP;
29import org.onlab.packet.ICMP6;
Jonathan Hart430223a2015-04-22 17:39:02 -070030import org.onlab.packet.IPv4;
31import org.onlab.packet.IPv6;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010032import org.onlab.packet.Ip4Prefix;
33import org.onlab.packet.Ip6Prefix;
Sahil Lelea69534f2015-07-16 15:14:57 -070034import org.onlab.packet.MacAddress;
Jonathan Hart430223a2015-04-22 17:39:02 -070035import org.onlab.packet.TCP;
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070036import org.onlab.packet.TpPort;
Jonathan Hart430223a2015-04-22 17:39:02 -070037import org.onlab.packet.UDP;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010038import org.onlab.packet.VlanId;
kalagesa1a1867a2017-03-07 13:06:41 +053039import org.onlab.util.KryoNamespace;
maojianwei8bf77b72016-02-15 15:18:24 +080040import org.onlab.util.Tools;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070041import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.core.ApplicationId;
43import org.onosproject.core.CoreService;
Sahil Lelea69534f2015-07-16 15:14:57 -070044import org.onosproject.event.Event;
45import org.onosproject.net.ConnectPoint;
46import org.onosproject.net.DeviceId;
Brian O'Connorabafb502014-12-02 22:26:20 -080047import org.onosproject.net.Host;
48import org.onosproject.net.HostId;
Sahil Lelea69534f2015-07-16 15:14:57 -070049import org.onosproject.net.Link;
Brian O'Connorabafb502014-12-02 22:26:20 -080050import org.onosproject.net.Path;
51import org.onosproject.net.PortNumber;
Brian O'Connorabafb502014-12-02 22:26:20 -080052import org.onosproject.net.flow.DefaultTrafficSelector;
53import org.onosproject.net.flow.DefaultTrafficTreatment;
Sahil Lelea69534f2015-07-16 15:14:57 -070054import org.onosproject.net.flow.FlowEntry;
55import org.onosproject.net.flow.FlowRule;
Brian O'Connorabafb502014-12-02 22:26:20 -080056import org.onosproject.net.flow.FlowRuleService;
57import org.onosproject.net.flow.TrafficSelector;
58import org.onosproject.net.flow.TrafficTreatment;
Sahil Lelea69534f2015-07-16 15:14:57 -070059import org.onosproject.net.flow.criteria.Criterion;
60import org.onosproject.net.flow.criteria.EthCriterion;
61import org.onosproject.net.flow.instructions.Instruction;
62import org.onosproject.net.flow.instructions.Instructions;
Jonathan Hart3b881aa2015-04-22 18:03:50 -070063import org.onosproject.net.flowobjective.DefaultForwardingObjective;
64import org.onosproject.net.flowobjective.FlowObjectiveService;
65import org.onosproject.net.flowobjective.ForwardingObjective;
Brian O'Connorabafb502014-12-02 22:26:20 -080066import org.onosproject.net.host.HostService;
Sahil Lelea69534f2015-07-16 15:14:57 -070067import org.onosproject.net.link.LinkEvent;
Brian O'Connorabafb502014-12-02 22:26:20 -080068import org.onosproject.net.packet.InboundPacket;
69import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080070import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080071import org.onosproject.net.packet.PacketProcessor;
72import org.onosproject.net.packet.PacketService;
Sahil Lelea69534f2015-07-16 15:14:57 -070073import org.onosproject.net.topology.TopologyEvent;
74import org.onosproject.net.topology.TopologyListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080075import org.onosproject.net.topology.TopologyService;
kalagesa1a1867a2017-03-07 13:06:41 +053076import org.onosproject.store.serializers.KryoNamespaces;
77import org.onosproject.store.service.EventuallyConsistentMap;
kalagesa1a1867a2017-03-07 13:06:41 +053078import org.onosproject.store.service.MultiValuedTimestamp;
Thomas Vachuskae9d21862017-10-03 14:05:48 -070079import org.onosproject.store.service.StorageService;
80import org.onosproject.store.service.WallClockTimestamp;
81import org.osgi.service.component.ComponentContext;
tomc370ebd2014-09-16 01:25:21 -070082import org.slf4j.Logger;
Thomas Vachuskae9d21862017-10-03 14:05:48 -070083
Jonathan Hart430223a2015-04-22 17:39:02 -070084import java.util.Dictionary;
Sahil Lele7b961662015-07-20 15:06:04 -070085import java.util.HashMap;
Sahil Lelea69534f2015-07-16 15:14:57 -070086import java.util.List;
Sahil Lele7b961662015-07-20 15:06:04 -070087import java.util.Map;
Sahil Lelea69534f2015-07-16 15:14:57 -070088import java.util.Objects;
Jonathan Hart430223a2015-04-22 17:39:02 -070089import java.util.Set;
Thomas Vachuskae9d21862017-10-03 14:05:48 -070090import java.util.concurrent.ExecutorService;
Jonathan Hart430223a2015-04-22 17:39:02 -070091
Thomas Vachuskae9d21862017-10-03 14:05:48 -070092import static java.util.concurrent.Executors.newSingleThreadExecutor;
93import static org.onlab.util.Tools.groupedThreads;
Jonathan Hart430223a2015-04-22 17:39:02 -070094import static org.slf4j.LoggerFactory.getLogger;
95
tomc370ebd2014-09-16 01:25:21 -070096/**
97 * Sample reactive forwarding application.
98 */
99@Component(immediate = true)
kalagesa1a1867a2017-03-07 13:06:41 +0530100@Service(value = ReactiveForwarding.class)
alshabib030111e2014-09-15 15:56:42 -0700101public class ReactiveForwarding {
102
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100103 private static final int DEFAULT_TIMEOUT = 10;
104 private static final int DEFAULT_PRIORITY = 10;
alshabibba5ac482014-10-02 17:15:20 -0700105
tomc370ebd2014-09-16 01:25:21 -0700106 private final Logger log = getLogger(getClass());
107
alshabib030111e2014-09-15 15:56:42 -0700108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected TopologyService topologyService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected PacketService packetService;
113
alshabib8aef1ad2014-09-15 17:47:31 -0700114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected HostService hostService;
116
alshabib7b2748f2014-09-16 20:21:11 -0700117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected FlowRuleService flowRuleService;
119
alshabib92c65ad2014-10-08 21:56:05 -0700120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700121 protected FlowObjectiveService flowObjectiveService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib92c65ad2014-10-08 21:56:05 -0700124 protected CoreService coreService;
125
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected ComponentConfigService cfgService;
128
kalagesa1a1867a2017-03-07 13:06:41 +0530129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected StorageService storageService;
131
tomc370ebd2014-09-16 01:25:21 -0700132 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -0700133
kalagesa1a1867a2017-03-07 13:06:41 +0530134 private EventuallyConsistentMap<MacAddress, ReactiveForwardMetrics> metrics;
135
alshabiba68eb962014-09-24 20:34:13 -0700136 private ApplicationId appId;
137
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800138 @Property(name = "packetOutOnly", boolValue = false,
139 label = "Enable packet-out only forwarding; default is false")
140 private boolean packetOutOnly = false;
tomc16656f2014-10-15 18:30:31 -0700141
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100142 @Property(name = "packetOutOfppTable", boolValue = false,
143 label = "Enable first packet forwarding using OFPP_TABLE port " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700144 "instead of PacketOut with actual port; default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100145 private boolean packetOutOfppTable = false;
146
147 @Property(name = "flowTimeout", intValue = DEFAULT_TIMEOUT,
148 label = "Configure Flow Timeout for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700149 "default is 10 sec")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100150 private int flowTimeout = DEFAULT_TIMEOUT;
151
152 @Property(name = "flowPriority", intValue = DEFAULT_PRIORITY,
153 label = "Configure Flow Priority for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700154 "default is 10")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100155 private int flowPriority = DEFAULT_PRIORITY;
156
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900157 @Property(name = "ipv6Forwarding", boolValue = false,
158 label = "Enable IPv6 forwarding; default is false")
159 private boolean ipv6Forwarding = false;
160
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100161 @Property(name = "matchDstMacOnly", boolValue = false,
162 label = "Enable matching Dst Mac Only; default is false")
163 private boolean matchDstMacOnly = false;
164
165 @Property(name = "matchVlanId", boolValue = false,
166 label = "Enable matching Vlan ID; default is false")
167 private boolean matchVlanId = false;
168
169 @Property(name = "matchIpv4Address", boolValue = false,
170 label = "Enable matching IPv4 Addresses; default is false")
171 private boolean matchIpv4Address = false;
172
173 @Property(name = "matchIpv4Dscp", boolValue = false,
174 label = "Enable matching IPv4 DSCP and ECN; default is false")
175 private boolean matchIpv4Dscp = false;
176
177 @Property(name = "matchIpv6Address", boolValue = false,
178 label = "Enable matching IPv6 Addresses; default is false")
179 private boolean matchIpv6Address = false;
180
181 @Property(name = "matchIpv6FlowLabel", boolValue = false,
182 label = "Enable matching IPv6 FlowLabel; default is false")
183 private boolean matchIpv6FlowLabel = false;
184
185 @Property(name = "matchTcpUdpPorts", boolValue = false,
186 label = "Enable matching TCP/UDP ports; default is false")
187 private boolean matchTcpUdpPorts = false;
188
189 @Property(name = "matchIcmpFields", boolValue = false,
190 label = "Enable matching ICMPv4 and ICMPv6 fields; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700191 "default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100192 private boolean matchIcmpFields = false;
193
Sahil Lelea69534f2015-07-16 15:14:57 -0700194
Rusty Eddy29acad62015-07-07 19:33:47 -0700195 @Property(name = "ignoreIPv4Multicast", boolValue = false,
196 label = "Ignore (do not forward) IPv4 multicast packets; default is false")
197 private boolean ignoreIpv4McastPackets = false;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100198
kalagesa1a1867a2017-03-07 13:06:41 +0530199 @Property(name = "recordMetrics", boolValue = false,
200 label = "Enable record metrics for reactive forwarding")
201 private boolean recordMetrics = false;
202
Sahil Lelea69534f2015-07-16 15:14:57 -0700203 private final TopologyListener topologyListener = new InternalTopologyListener();
204
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700205 private ExecutorService blackHoleExecutor;
206
Sahil Lelea69534f2015-07-16 15:14:57 -0700207
alshabib030111e2014-09-15 15:56:42 -0700208 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900209 public void activate(ComponentContext context) {
kalagesa1a1867a2017-03-07 13:06:41 +0530210 KryoNamespace.Builder metricSerializer = KryoNamespace.newBuilder()
211 .register(KryoNamespaces.API)
212 .register(ReactiveForwardMetrics.class)
213 .register(MultiValuedTimestamp.class);
214 metrics = storageService.<MacAddress, ReactiveForwardMetrics>eventuallyConsistentMapBuilder()
215 .withName("metrics-fwd")
216 .withSerializer(metricSerializer)
217 .withTimestampProvider((key, metricsData) -> new
218 MultiValuedTimestamp<>(new WallClockTimestamp(), System.nanoTime()))
219 .build();
220
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700221 blackHoleExecutor = newSingleThreadExecutor(groupedThreads("onos/app/fwd",
222 "black-hole-fixer",
223 log));
224
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700225 cfgService.registerProperties(getClass());
Brian O'Connorabafb502014-12-02 22:26:20 -0800226 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800227
Brian O'Connor3b783262015-07-29 17:49:24 -0700228 packetService.addProcessor(processor, PacketProcessor.director(2));
Sahil Lelea69534f2015-07-16 15:14:57 -0700229 topologyService.addListener(topologyListener);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900230 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700231 requestIntercepts();
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800232
Thomas Vachuska8d033672015-07-21 16:15:04 -0700233 log.info("Started", appId.id());
Charles M.C. Chane148de82015-05-06 12:38:21 +0800234 }
235
236 @Deactivate
237 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800238 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700239 withdrawIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800240 flowRuleService.removeFlowRulesById(appId);
241 packetService.removeProcessor(processor);
Sahil Lelea69534f2015-07-16 15:14:57 -0700242 topologyService.removeListener(topologyListener);
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700243 blackHoleExecutor.shutdown();
244 blackHoleExecutor = null;
Charles M.C. Chane148de82015-05-06 12:38:21 +0800245 processor = null;
246 log.info("Stopped");
247 }
248
249 @Modified
250 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800251 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700252 requestIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800253 }
254
255 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700256 * Request packet in via packet service.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800257 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700258 private void requestIntercepts() {
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800259 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
260 selector.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700261 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100262
Thomas Vachuska27bee092015-06-23 19:03:10 -0700263 selector.matchEthType(Ethernet.TYPE_IPV6);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100264 if (ipv6Forwarding) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700265 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
266 } else {
267 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100268 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900269 }
270
271 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700272 * Cancel request for packet in via packet service.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700273 */
274 private void withdrawIntercepts() {
275 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
276 selector.matchEthType(Ethernet.TYPE_IPV4);
277 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700278 selector.matchEthType(Ethernet.TYPE_IPV6);
279 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
280 }
281
282 /**
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900283 * Extracts properties from the component configuration context.
284 *
285 * @param context the component context
286 */
287 private void readComponentConfiguration(ComponentContext context) {
288 Dictionary<?, ?> properties = context.getProperties();
Jian Lid9b5f552016-03-11 18:15:31 -0800289
290 Boolean packetOutOnlyEnabled =
291 Tools.isPropertyEnabled(properties, "packetOutOnly");
292 if (packetOutOnlyEnabled == null) {
293 log.info("Packet-out is not configured, " +
294 "using current value of {}", packetOutOnly);
295 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900296 packetOutOnly = packetOutOnlyEnabled;
297 log.info("Configured. Packet-out only forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800298 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700299 }
Jian Lid9b5f552016-03-11 18:15:31 -0800300
301 Boolean packetOutOfppTableEnabled =
302 Tools.isPropertyEnabled(properties, "packetOutOfppTable");
303 if (packetOutOfppTableEnabled == null) {
304 log.info("OFPP_TABLE port is not configured, " +
305 "using current value of {}", packetOutOfppTable);
306 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100307 packetOutOfppTable = packetOutOfppTableEnabled;
308 log.info("Configured. Forwarding using OFPP_TABLE port is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800309 packetOutOfppTable ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100310 }
Jian Lid9b5f552016-03-11 18:15:31 -0800311
312 Boolean ipv6ForwardingEnabled =
313 Tools.isPropertyEnabled(properties, "ipv6Forwarding");
314 if (ipv6ForwardingEnabled == null) {
315 log.info("IPv6 forwarding is not configured, " +
316 "using current value of {}", ipv6Forwarding);
317 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900318 ipv6Forwarding = ipv6ForwardingEnabled;
319 log.info("Configured. IPv6 forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800320 ipv6Forwarding ? "enabled" : "disabled");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900321 }
Jian Lid9b5f552016-03-11 18:15:31 -0800322
323 Boolean matchDstMacOnlyEnabled =
324 Tools.isPropertyEnabled(properties, "matchDstMacOnly");
325 if (matchDstMacOnlyEnabled == null) {
326 log.info("Match Dst MAC is not configured, " +
327 "using current value of {}", matchDstMacOnly);
328 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100329 matchDstMacOnly = matchDstMacOnlyEnabled;
330 log.info("Configured. Match Dst MAC Only is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800331 matchDstMacOnly ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100332 }
Jian Lid9b5f552016-03-11 18:15:31 -0800333
334 Boolean matchVlanIdEnabled =
335 Tools.isPropertyEnabled(properties, "matchVlanId");
336 if (matchVlanIdEnabled == null) {
337 log.info("Matching Vlan ID is not configured, " +
338 "using current value of {}", matchVlanId);
339 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100340 matchVlanId = matchVlanIdEnabled;
341 log.info("Configured. Matching Vlan ID is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800342 matchVlanId ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100343 }
Jian Lid9b5f552016-03-11 18:15:31 -0800344
345 Boolean matchIpv4AddressEnabled =
346 Tools.isPropertyEnabled(properties, "matchIpv4Address");
347 if (matchIpv4AddressEnabled == null) {
348 log.info("Matching IPv4 Address is not configured, " +
349 "using current value of {}", matchIpv4Address);
350 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100351 matchIpv4Address = matchIpv4AddressEnabled;
352 log.info("Configured. Matching IPv4 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800353 matchIpv4Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100354 }
Jian Lid9b5f552016-03-11 18:15:31 -0800355
356 Boolean matchIpv4DscpEnabled =
357 Tools.isPropertyEnabled(properties, "matchIpv4Dscp");
358 if (matchIpv4DscpEnabled == null) {
359 log.info("Matching IPv4 DSCP and ECN is not configured, " +
360 "using current value of {}", matchIpv4Dscp);
361 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100362 matchIpv4Dscp = matchIpv4DscpEnabled;
363 log.info("Configured. Matching IPv4 DSCP and ECN is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800364 matchIpv4Dscp ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100365 }
Jian Lid9b5f552016-03-11 18:15:31 -0800366
367 Boolean matchIpv6AddressEnabled =
368 Tools.isPropertyEnabled(properties, "matchIpv6Address");
369 if (matchIpv6AddressEnabled == null) {
370 log.info("Matching IPv6 Address is not configured, " +
371 "using current value of {}", matchIpv6Address);
372 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100373 matchIpv6Address = matchIpv6AddressEnabled;
374 log.info("Configured. Matching IPv6 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800375 matchIpv6Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100376 }
Jian Lid9b5f552016-03-11 18:15:31 -0800377
378 Boolean matchIpv6FlowLabelEnabled =
379 Tools.isPropertyEnabled(properties, "matchIpv6FlowLabel");
380 if (matchIpv6FlowLabelEnabled == null) {
381 log.info("Matching IPv6 FlowLabel is not configured, " +
382 "using current value of {}", matchIpv6FlowLabel);
383 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100384 matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
385 log.info("Configured. Matching IPv6 FlowLabel is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800386 matchIpv6FlowLabel ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100387 }
Jian Lid9b5f552016-03-11 18:15:31 -0800388
389 Boolean matchTcpUdpPortsEnabled =
390 Tools.isPropertyEnabled(properties, "matchTcpUdpPorts");
391 if (matchTcpUdpPortsEnabled == null) {
392 log.info("Matching TCP/UDP fields is not configured, " +
393 "using current value of {}", matchTcpUdpPorts);
394 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100395 matchTcpUdpPorts = matchTcpUdpPortsEnabled;
396 log.info("Configured. Matching TCP/UDP fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800397 matchTcpUdpPorts ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100398 }
Jian Lid9b5f552016-03-11 18:15:31 -0800399
400 Boolean matchIcmpFieldsEnabled =
401 Tools.isPropertyEnabled(properties, "matchIcmpFields");
402 if (matchIcmpFieldsEnabled == null) {
403 log.info("Matching ICMP (v4 and v6) fields is not configured, " +
404 "using current value of {}", matchIcmpFields);
405 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100406 matchIcmpFields = matchIcmpFieldsEnabled;
407 log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800408 matchIcmpFields ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100409 }
Rusty Eddy29acad62015-07-07 19:33:47 -0700410
Jian Lid9b5f552016-03-11 18:15:31 -0800411 Boolean ignoreIpv4McastPacketsEnabled =
412 Tools.isPropertyEnabled(properties, "ignoreIpv4McastPackets");
413 if (ignoreIpv4McastPacketsEnabled == null) {
414 log.info("Ignore IPv4 multi-cast packet is not configured, " +
415 "using current value of {}", ignoreIpv4McastPackets);
416 } else {
Rusty Eddy29acad62015-07-07 19:33:47 -0700417 ignoreIpv4McastPackets = ignoreIpv4McastPacketsEnabled;
418 log.info("Configured. Ignore IPv4 multicast packets is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800419 ignoreIpv4McastPackets ? "enabled" : "disabled");
Rusty Eddy29acad62015-07-07 19:33:47 -0700420 }
kalagesa1a1867a2017-03-07 13:06:41 +0530421 Boolean recordMetricsEnabled =
422 Tools.isPropertyEnabled(properties, "recordMetrics");
423 if (recordMetricsEnabled == null) {
424 log.info("IConfigured. Ignore record metrics is {} ," +
425 "using current value of {}", recordMetrics);
426 } else {
427 recordMetrics = recordMetricsEnabled;
428 log.info("Configured. record metrics is {}",
429 recordMetrics ? "enabled" : "disabled");
430 }
431
Jian Lid9b5f552016-03-11 18:15:31 -0800432 flowTimeout = Tools.getIntegerProperty(properties, "flowTimeout", DEFAULT_TIMEOUT);
Yoonseon Han2f011ad2016-08-08 17:06:56 -0700433 log.info("Configured. Flow Timeout is configured to {} seconds", flowTimeout);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100434
Jian Lid9b5f552016-03-11 18:15:31 -0800435 flowPriority = Tools.getIntegerProperty(properties, "flowPriority", DEFAULT_PRIORITY);
436 log.info("Configured. Flow Priority is configured to {}", flowPriority);
tomc16656f2014-10-15 18:30:31 -0700437 }
tomc370ebd2014-09-16 01:25:21 -0700438
439 /**
440 * Packet processor responsible for forwarding packets along their paths.
441 */
442 private class ReactivePacketProcessor implements PacketProcessor {
443
444 @Override
445 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700446 // Stop processing if the packet has been handled, since we
447 // can't do any more to it.
Sahil Lelea69534f2015-07-16 15:14:57 -0700448
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800449 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700450 return;
451 }
tomdc95b8a2014-09-17 08:07:26 -0700452
tomc370ebd2014-09-16 01:25:21 -0700453 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700454 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700455
Jonathan Harte8600eb2015-01-12 10:30:45 -0800456 if (ethPkt == null) {
457 return;
458 }
459
kalagesa1a1867a2017-03-07 13:06:41 +0530460 MacAddress macAddress = ethPkt.getSourceMAC();
461 ReactiveForwardMetrics macMetrics = null;
462 macMetrics = createCounter(macAddress);
463 inPacket(macMetrics);
464
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900465 // Bail if this is deemed to be a control packet.
466 if (isControlPacket(ethPkt)) {
kalagesa1a1867a2017-03-07 13:06:41 +0530467 droppedPacket(macMetrics);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900468 return;
469 }
470
471 // Skip IPv6 multicast packet when IPv6 forward is disabled.
472 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
kalagesa1a1867a2017-03-07 13:06:41 +0530473 droppedPacket(macMetrics);
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800474 return;
475 }
476
tom642b2262014-09-17 13:52:55 -0700477 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700478
Charles Chan928ff8b2017-05-04 12:22:54 -0700479 // Do not process LLDP MAC address in any way.
480 if (id.mac().isLldp()) {
kalagesa1a1867a2017-03-07 13:06:41 +0530481 droppedPacket(macMetrics);
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700482 return;
483 }
484
Rusty Eddy29acad62015-07-07 19:33:47 -0700485 // Do not process IPv4 multicast packets, let mfwd handle them
486 if (ignoreIpv4McastPackets && ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
487 if (id.mac().isMulticast()) {
488 return;
489 }
490 }
491
tomc370ebd2014-09-16 01:25:21 -0700492 // Do we know who this is for? If not, flood and bail.
493 Host dst = hostService.getHost(id);
494 if (dst == null) {
kalagesa1a1867a2017-03-07 13:06:41 +0530495 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700496 return;
497 }
498
499 // Are we on an edge switch that our destination is on? If so,
500 // simply forward out to the destination and bail.
501 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700502 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
kalagesa1a1867a2017-03-07 13:06:41 +0530503 installRule(context, dst.location().port(), macMetrics);
alshabib6eb438a2014-10-01 16:39:37 -0700504 }
tomc370ebd2014-09-16 01:25:21 -0700505 return;
506 }
507
508 // Otherwise, get a set of paths that lead from here to the
509 // destination edge switch.
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100510 Set<Path> paths =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700511 topologyService.getPaths(topologyService.currentTopology(),
512 pkt.receivedFrom().deviceId(),
513 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700514 if (paths.isEmpty()) {
515 // If there are no paths, flood and bail.
kalagesa1a1867a2017-03-07 13:06:41 +0530516 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700517 return;
518 }
519
520 // Otherwise, pick a path that does not lead back to where we
521 // came from; if no such path, flood and bail.
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700522 Path path = pickForwardPathIfPossible(paths, pkt.receivedFrom().port());
tomc370ebd2014-09-16 01:25:21 -0700523 if (path == null) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700524 log.warn("Don't know where to go from here {} for {} -> {}",
525 pkt.receivedFrom(), ethPkt.getSourceMAC(), ethPkt.getDestinationMAC());
kalagesa1a1867a2017-03-07 13:06:41 +0530526 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700527 return;
528 }
529
530 // Otherwise forward and be done with it.
kalagesa1a1867a2017-03-07 13:06:41 +0530531 installRule(context, path.src().port(), macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700532 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800533
534 }
535
536 // Indicates whether this is a control packet, e.g. LLDP, BDDP
537 private boolean isControlPacket(Ethernet eth) {
538 short type = eth.getEtherType();
539 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700540 }
541
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800542 // Indicated whether this is an IPv6 multicast packet.
543 private boolean isIpv6Multicast(Ethernet eth) {
544 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
545 }
546
tomc370ebd2014-09-16 01:25:21 -0700547 // Selects a path from the given set that does not lead back to the
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700548 // specified port if possible.
549 private Path pickForwardPathIfPossible(Set<Path> paths, PortNumber notToPort) {
tomc370ebd2014-09-16 01:25:21 -0700550 for (Path path : paths) {
551 if (!path.src().port().equals(notToPort)) {
552 return path;
553 }
554 }
Cheng-An Chuangd0a70f52017-08-10 21:20:32 +0800555 return null;
tomc370ebd2014-09-16 01:25:21 -0700556 }
557
tom642b2262014-09-17 13:52:55 -0700558 // Floods the specified packet if permissible.
kalagesa1a1867a2017-03-07 13:06:41 +0530559 private void flood(PacketContext context, ReactiveForwardMetrics macMetrics) {
tomdc95b8a2014-09-17 08:07:26 -0700560 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700561 context.inPacket().receivedFrom())) {
kalagesa1a1867a2017-03-07 13:06:41 +0530562 packetOut(context, PortNumber.FLOOD, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700563 } else {
564 context.block();
565 }
566 }
567
tom642b2262014-09-17 13:52:55 -0700568 // Sends a packet out the specified port.
kalagesa1a1867a2017-03-07 13:06:41 +0530569 private void packetOut(PacketContext context, PortNumber portNumber, ReactiveForwardMetrics macMetrics) {
570 replyPacket(macMetrics);
alshabib010c31d2014-09-26 10:01:12 -0700571 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700572 context.send();
573 }
574
575 // Install a rule forwarding the packet to the specified port.
kalagesa1a1867a2017-03-07 13:06:41 +0530576 private void installRule(PacketContext context, PortNumber portNumber, ReactiveForwardMetrics macMetrics) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100577 //
578 // We don't support (yet) buffer IDs in the Flow Service so
579 // packet out first.
580 //
581 Ethernet inPkt = context.inPacket().parsed();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700582 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100583
584 // If PacketOutOnly or ARP packet than forward directly to output port
585 if (packetOutOnly || inPkt.getEtherType() == Ethernet.TYPE_ARP) {
kalagesa1a1867a2017-03-07 13:06:41 +0530586 packetOut(context, portNumber, macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100587 return;
588 }
589
590 //
591 // If matchDstMacOnly
592 // Create flows matching dstMac only
593 // Else
594 // Create flows with default matching and include configured fields
595 //
596 if (matchDstMacOnly) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700597 selectorBuilder.matchEthDst(inPkt.getDestinationMAC());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100598 } else {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700599 selectorBuilder.matchInPort(context.inPacket().receivedFrom().port())
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800600 .matchEthSrc(inPkt.getSourceMAC())
Jonathan Hart430223a2015-04-22 17:39:02 -0700601 .matchEthDst(inPkt.getDestinationMAC());
alshabib7b2748f2014-09-16 20:21:11 -0700602
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100603 // If configured Match Vlan ID
604 if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700605 selectorBuilder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100606 }
alshabib7b2748f2014-09-16 20:21:11 -0700607
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100608 //
609 // If configured and EtherType is IPv4 - Match IPv4 and
610 // TCP/UDP/ICMP fields
611 //
612 if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
613 IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
614 byte ipv4Protocol = ipv4Packet.getProtocol();
615 Ip4Prefix matchIp4SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700616 Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
617 Ip4Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100618 Ip4Prefix matchIp4DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700619 Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
620 Ip4Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800621 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
Jonathan Hart430223a2015-04-22 17:39:02 -0700622 .matchIPSrc(matchIp4SrcPrefix)
623 .matchIPDst(matchIp4DstPrefix);
alshabib6eb438a2014-10-01 16:39:37 -0700624
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100625 if (matchIpv4Dscp) {
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -0800626 byte dscp = ipv4Packet.getDscp();
627 byte ecn = ipv4Packet.getEcn();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700628 selectorBuilder.matchIPDscp(dscp).matchIPEcn(ecn);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100629 }
630
631 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
632 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700633 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700634 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
635 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100636 }
637 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
638 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700639 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700640 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
641 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100642 }
643 if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
644 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700645 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700646 .matchIcmpType(icmpPacket.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100647 .matchIcmpCode(icmpPacket.getIcmpCode());
648 }
649 }
650
651 //
652 // If configured and EtherType is IPv6 - Match IPv6 and
653 // TCP/UDP/ICMP fields
654 //
655 if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
656 IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
657 byte ipv6NextHeader = ipv6Packet.getNextHeader();
658 Ip6Prefix matchIp6SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700659 Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
660 Ip6Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100661 Ip6Prefix matchIp6DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700662 Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
663 Ip6Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800664 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6)
665 .matchIPv6Src(matchIp6SrcPrefix)
Jonathan Hart430223a2015-04-22 17:39:02 -0700666 .matchIPv6Dst(matchIp6DstPrefix);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100667
668 if (matchIpv6FlowLabel) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700669 selectorBuilder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100670 }
671
672 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
673 TCP tcpPacket = (TCP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700674 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700675 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
676 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100677 }
678 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
679 UDP udpPacket = (UDP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700680 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700681 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
682 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100683 }
684 if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
685 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700686 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700687 .matchIcmpv6Type(icmp6Packet.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100688 .matchIcmpv6Code(icmp6Packet.getIcmpCode());
689 }
690 }
691 }
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700692 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
693 .setOutput(portNumber)
694 .build();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100695
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700696 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
697 .withSelector(selectorBuilder.build())
698 .withTreatment(treatment)
699 .withPriority(flowPriority)
700 .withFlag(ForwardingObjective.Flag.VERSATILE)
701 .fromApp(appId)
702 .makeTemporary(flowTimeout)
703 .add();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100704
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700705 flowObjectiveService.forward(context.inPacket().receivedFrom().deviceId(),
706 forwardingObjective);
kalagesa1a1867a2017-03-07 13:06:41 +0530707 forwardPacket(macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100708 //
709 // If packetOutOfppTable
710 // Send packet back to the OpenFlow pipeline to match installed flow
711 // Else
712 // Send packet direction on the appropriate port
713 //
714 if (packetOutOfppTable) {
kalagesa1a1867a2017-03-07 13:06:41 +0530715 packetOut(context, PortNumber.TABLE, macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100716 } else {
kalagesa1a1867a2017-03-07 13:06:41 +0530717 packetOut(context, portNumber, macMetrics);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800718 }
tomc370ebd2014-09-16 01:25:21 -0700719 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700720
kalagesa1a1867a2017-03-07 13:06:41 +0530721
Sahil Lelea69534f2015-07-16 15:14:57 -0700722 private class InternalTopologyListener implements TopologyListener {
723 @Override
724 public void event(TopologyEvent event) {
725 List<Event> reasons = event.reasons();
726 if (reasons != null) {
727 reasons.forEach(re -> {
728 if (re instanceof LinkEvent) {
729 LinkEvent le = (LinkEvent) re;
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700730 if (le.type() == LinkEvent.Type.LINK_REMOVED && blackHoleExecutor != null) {
731 blackHoleExecutor.submit(() -> fixBlackhole(le.subject().src()));
Sahil Lelea69534f2015-07-16 15:14:57 -0700732 }
733 }
734 });
735 }
736 }
737 }
738
739 private void fixBlackhole(ConnectPoint egress) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700740 Set<FlowEntry> rules = getFlowRulesFrom(egress);
Sahil Lelea69534f2015-07-16 15:14:57 -0700741 Set<SrcDstPair> pairs = findSrcDstPairs(rules);
742
Sahil Lele7b961662015-07-20 15:06:04 -0700743 Map<DeviceId, Set<Path>> srcPaths = new HashMap<>();
744
Thomas Vachuska8d033672015-07-21 16:15:04 -0700745 for (SrcDstPair sd : pairs) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700746 // get the edge deviceID for the src host
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700747 Host srcHost = hostService.getHost(HostId.hostId(sd.src));
748 Host dstHost = hostService.getHost(HostId.hostId(sd.dst));
749 if (srcHost != null && dstHost != null) {
750 DeviceId srcId = srcHost.location().deviceId();
751 DeviceId dstId = dstHost.location().deviceId();
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800752 log.trace("SRC ID is {}, DST ID is {}", srcId, dstId);
Sahil Lelea69534f2015-07-16 15:14:57 -0700753
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700754 cleanFlowRules(sd, egress.deviceId());
Sahil Lelea69534f2015-07-16 15:14:57 -0700755
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700756 Set<Path> shortestPaths = srcPaths.get(srcId);
757 if (shortestPaths == null) {
758 shortestPaths = topologyService.getPaths(topologyService.currentTopology(),
759 egress.deviceId(), srcId);
760 srcPaths.put(srcId, shortestPaths);
761 }
762 backTrackBadNodes(shortestPaths, dstId, sd);
Sahil Lele7b961662015-07-20 15:06:04 -0700763 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700764 }
765 }
766
767 // Backtracks from link down event to remove flows that lead to blackhole
768 private void backTrackBadNodes(Set<Path> shortestPaths, DeviceId dstId, SrcDstPair sd) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700769 for (Path p : shortestPaths) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700770 List<Link> pathLinks = p.links();
771 for (int i = 0; i < pathLinks.size(); i = i + 1) {
772 Link curLink = pathLinks.get(i);
773 DeviceId curDevice = curLink.src().deviceId();
Sahil Lelea69534f2015-07-16 15:14:57 -0700774
775 // skipping the first link because this link's src has already been pruned beforehand
776 if (i != 0) {
777 cleanFlowRules(sd, curDevice);
778 }
779
Thomas Vachuska8d033672015-07-21 16:15:04 -0700780 Set<Path> pathsFromCurDevice =
781 topologyService.getPaths(topologyService.currentTopology(),
782 curDevice, dstId);
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700783 if (pickForwardPathIfPossible(pathsFromCurDevice, curLink.src().port()) != null) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700784 break;
785 } else {
786 if (i + 1 == pathLinks.size()) {
787 cleanFlowRules(sd, curLink.dst().deviceId());
788 }
789 }
790 }
791 }
792 }
793
794 // Removes flow rules off specified device with specific SrcDstPair
795 private void cleanFlowRules(SrcDstPair pair, DeviceId id) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800796 log.trace("Searching for flow rules to remove from: {}", id);
797 log.trace("Removing flows w/ SRC={}, DST={}", pair.src, pair.dst);
Sahil Lelea69534f2015-07-16 15:14:57 -0700798 for (FlowEntry r : flowRuleService.getFlowEntries(id)) {
799 boolean matchesSrc = false, matchesDst = false;
800 for (Instruction i : r.treatment().allInstructions()) {
801 if (i.type() == Instruction.Type.OUTPUT) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700802 // if the flow has matching src and dst
Sahil Lelea69534f2015-07-16 15:14:57 -0700803 for (Criterion cr : r.selector().criteria()) {
804 if (cr.type() == Criterion.Type.ETH_DST) {
805 if (((EthCriterion) cr).mac().equals(pair.dst)) {
806 matchesDst = true;
807 }
808 } else if (cr.type() == Criterion.Type.ETH_SRC) {
809 if (((EthCriterion) cr).mac().equals(pair.src)) {
810 matchesSrc = true;
811 }
812 }
813 }
814 }
815 }
816 if (matchesDst && matchesSrc) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800817 log.trace("Removed flow rule from device: {}", id);
Sahil Lelea69534f2015-07-16 15:14:57 -0700818 flowRuleService.removeFlowRules((FlowRule) r);
819 }
820 }
821
822 }
823
824 // Returns a set of src/dst MAC pairs extracted from the specified set of flow entries
825 private Set<SrcDstPair> findSrcDstPairs(Set<FlowEntry> rules) {
826 ImmutableSet.Builder<SrcDstPair> builder = ImmutableSet.builder();
Thomas Vachuska8d033672015-07-21 16:15:04 -0700827 for (FlowEntry r : rules) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700828 MacAddress src = null, dst = null;
Thomas Vachuska8d033672015-07-21 16:15:04 -0700829 for (Criterion cr : r.selector().criteria()) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700830 if (cr.type() == Criterion.Type.ETH_DST) {
831 dst = ((EthCriterion) cr).mac();
832 } else if (cr.type() == Criterion.Type.ETH_SRC) {
833 src = ((EthCriterion) cr).mac();
834 }
835 }
836 builder.add(new SrcDstPair(src, dst));
837 }
838 return builder.build();
839 }
840
kalagesa1a1867a2017-03-07 13:06:41 +0530841 private ReactiveForwardMetrics createCounter(MacAddress macAddress) {
842 ReactiveForwardMetrics macMetrics = null;
843 if (recordMetrics) {
844 macMetrics = metrics.compute(macAddress, (key, existingValue) -> {
845 if (existingValue == null) {
846 return new ReactiveForwardMetrics(0L, 0L, 0L, 0L, macAddress);
847 } else {
848 return existingValue;
849 }
850 });
851 }
852 return macMetrics;
853 }
854
855 private void forwardPacket(ReactiveForwardMetrics macmetrics) {
856 if (recordMetrics) {
857 macmetrics.incrementForwardedPacket();
858 metrics.put(macmetrics.getMacAddress(), macmetrics);
859 }
860 }
861
862 private void inPacket(ReactiveForwardMetrics macmetrics) {
863 if (recordMetrics) {
864 macmetrics.incrementInPacket();
865 metrics.put(macmetrics.getMacAddress(), macmetrics);
866 }
867 }
868
869 private void replyPacket(ReactiveForwardMetrics macmetrics) {
870 if (recordMetrics) {
871 macmetrics.incremnetReplyPacket();
872 metrics.put(macmetrics.getMacAddress(), macmetrics);
873 }
874 }
875
876 private void droppedPacket(ReactiveForwardMetrics macmetrics) {
877 if (recordMetrics) {
878 macmetrics.incrementDroppedPacket();
879 metrics.put(macmetrics.getMacAddress(), macmetrics);
880 }
881 }
882
883 public EventuallyConsistentMap<MacAddress, ReactiveForwardMetrics> getMacAddress() {
884 return metrics;
885 }
886
887 public void printMetric(MacAddress mac) {
888 System.out.println("-----------------------------------------------------------------------------------------");
889 System.out.println(" MACADDRESS \t\t\t\t\t\t Metrics");
890 if (mac != null) {
891 System.out.println(" " + mac + " \t\t\t " + metrics.get(mac));
892 } else {
893 for (MacAddress key : metrics.keySet()) {
894 System.out.println(" " + key + " \t\t\t " + metrics.get(key));
895 }
896 }
897 }
898
Sahil Lelea69534f2015-07-16 15:14:57 -0700899 private Set<FlowEntry> getFlowRulesFrom(ConnectPoint egress) {
900 ImmutableSet.Builder<FlowEntry> builder = ImmutableSet.builder();
901 flowRuleService.getFlowEntries(egress.deviceId()).forEach(r -> {
902 if (r.appId() == appId.id()) {
903 r.treatment().allInstructions().forEach(i -> {
904 if (i.type() == Instruction.Type.OUTPUT) {
905 if (((Instructions.OutputInstruction) i).port().equals(egress.port())) {
906 builder.add(r);
907 }
908 }
909 });
910 }
911 });
912
913 return builder.build();
914 }
915
916 // Wrapper class for a source and destination pair of MAC addresses
917 private final class SrcDstPair {
918 final MacAddress src;
919 final MacAddress dst;
920
921 private SrcDstPair(MacAddress src, MacAddress dst) {
922 this.src = src;
923 this.dst = dst;
924 }
925
926 @Override
927 public boolean equals(Object o) {
928 if (this == o) {
929 return true;
930 }
931 if (o == null || getClass() != o.getClass()) {
932 return false;
933 }
934 SrcDstPair that = (SrcDstPair) o;
935 return Objects.equals(src, that.src) &&
936 Objects.equals(dst, that.dst);
937 }
938
939 @Override
940 public int hashCode() {
941 return Objects.hash(src, dst);
942 }
943 }
alshabib030111e2014-09-15 15:56:42 -0700944}