blob: 4b7ce6733f86ab6c42b48502ef847aa3c276a079 [file] [log] [blame]
Thomas Vachuska781d18b2014-10-27 10:31:25 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2014-present Open Networking Laboratory
Thomas Vachuska781d18b2014-10-27 10:31:25 -07003 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07004 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
Thomas Vachuska781d18b2014-10-27 10:31:25 -07007 *
Thomas Vachuska4f1a60c2014-10-28 13:39:07 -07008 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
Thomas Vachuska781d18b2014-10-27 10:31:25 -070015 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.fwd;
alshabib030111e2014-09-15 15:56:42 -070017
Sahil Lelea69534f2015-07-16 15:14:57 -070018import com.google.common.collect.ImmutableSet;
alshabib030111e2014-09-15 15:56:42 -070019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
tomc16656f2014-10-15 18:30:31 -070022import org.apache.felix.scr.annotations.Modified;
23import org.apache.felix.scr.annotations.Property;
alshabib030111e2014-09-15 15:56:42 -070024import org.apache.felix.scr.annotations.Reference;
kalagesa1a1867a2017-03-07 13:06:41 +053025import org.apache.felix.scr.annotations.Service;
alshabib030111e2014-09-15 15:56:42 -070026import org.apache.felix.scr.annotations.ReferenceCardinality;
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.service.StorageService;
tomc16656f2014-10-15 18:30:31 -070077import org.osgi.service.component.ComponentContext;
kalagesa1a1867a2017-03-07 13:06:41 +053078import org.onosproject.store.serializers.KryoNamespaces;
79import org.onosproject.store.service.EventuallyConsistentMap;
80import org.onosproject.store.service.WallClockTimestamp;
81import org.onosproject.store.service.MultiValuedTimestamp;
tomc370ebd2014-09-16 01:25:21 -070082import org.slf4j.Logger;
Jonathan Hart430223a2015-04-22 17:39:02 -070083import java.util.Dictionary;
Sahil Lele7b961662015-07-20 15:06:04 -070084import java.util.HashMap;
Sahil Lelea69534f2015-07-16 15:14:57 -070085import java.util.List;
Sahil Lele7b961662015-07-20 15:06:04 -070086import java.util.Map;
Sahil Lelea69534f2015-07-16 15:14:57 -070087import java.util.Objects;
Jonathan Hart430223a2015-04-22 17:39:02 -070088import java.util.Set;
89
Jonathan Hart430223a2015-04-22 17:39:02 -070090import static org.slf4j.LoggerFactory.getLogger;
91
tomc370ebd2014-09-16 01:25:21 -070092/**
93 * Sample reactive forwarding application.
94 */
95@Component(immediate = true)
kalagesa1a1867a2017-03-07 13:06:41 +053096@Service(value = ReactiveForwarding.class)
alshabib030111e2014-09-15 15:56:42 -070097public class ReactiveForwarding {
98
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010099 private static final int DEFAULT_TIMEOUT = 10;
100 private static final int DEFAULT_PRIORITY = 10;
alshabibba5ac482014-10-02 17:15:20 -0700101
tomc370ebd2014-09-16 01:25:21 -0700102 private final Logger log = getLogger(getClass());
103
alshabib030111e2014-09-15 15:56:42 -0700104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected TopologyService topologyService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected PacketService packetService;
109
alshabib8aef1ad2014-09-15 17:47:31 -0700110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected HostService hostService;
112
alshabib7b2748f2014-09-16 20:21:11 -0700113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected FlowRuleService flowRuleService;
115
alshabib92c65ad2014-10-08 21:56:05 -0700116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700117 protected FlowObjectiveService flowObjectiveService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib92c65ad2014-10-08 21:56:05 -0700120 protected CoreService coreService;
121
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected ComponentConfigService cfgService;
124
kalagesa1a1867a2017-03-07 13:06:41 +0530125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected StorageService storageService;
127
tomc370ebd2014-09-16 01:25:21 -0700128 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -0700129
kalagesa1a1867a2017-03-07 13:06:41 +0530130 private EventuallyConsistentMap<MacAddress, ReactiveForwardMetrics> metrics;
131
alshabiba68eb962014-09-24 20:34:13 -0700132 private ApplicationId appId;
133
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800134 @Property(name = "packetOutOnly", boolValue = false,
135 label = "Enable packet-out only forwarding; default is false")
136 private boolean packetOutOnly = false;
tomc16656f2014-10-15 18:30:31 -0700137
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100138 @Property(name = "packetOutOfppTable", boolValue = false,
139 label = "Enable first packet forwarding using OFPP_TABLE port " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700140 "instead of PacketOut with actual port; default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100141 private boolean packetOutOfppTable = false;
142
143 @Property(name = "flowTimeout", intValue = DEFAULT_TIMEOUT,
144 label = "Configure Flow Timeout for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700145 "default is 10 sec")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100146 private int flowTimeout = DEFAULT_TIMEOUT;
147
148 @Property(name = "flowPriority", intValue = DEFAULT_PRIORITY,
149 label = "Configure Flow Priority for installed flow rules; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700150 "default is 10")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100151 private int flowPriority = DEFAULT_PRIORITY;
152
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900153 @Property(name = "ipv6Forwarding", boolValue = false,
154 label = "Enable IPv6 forwarding; default is false")
155 private boolean ipv6Forwarding = false;
156
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100157 @Property(name = "matchDstMacOnly", boolValue = false,
158 label = "Enable matching Dst Mac Only; default is false")
159 private boolean matchDstMacOnly = false;
160
161 @Property(name = "matchVlanId", boolValue = false,
162 label = "Enable matching Vlan ID; default is false")
163 private boolean matchVlanId = false;
164
165 @Property(name = "matchIpv4Address", boolValue = false,
166 label = "Enable matching IPv4 Addresses; default is false")
167 private boolean matchIpv4Address = false;
168
169 @Property(name = "matchIpv4Dscp", boolValue = false,
170 label = "Enable matching IPv4 DSCP and ECN; default is false")
171 private boolean matchIpv4Dscp = false;
172
173 @Property(name = "matchIpv6Address", boolValue = false,
174 label = "Enable matching IPv6 Addresses; default is false")
175 private boolean matchIpv6Address = false;
176
177 @Property(name = "matchIpv6FlowLabel", boolValue = false,
178 label = "Enable matching IPv6 FlowLabel; default is false")
179 private boolean matchIpv6FlowLabel = false;
180
181 @Property(name = "matchTcpUdpPorts", boolValue = false,
182 label = "Enable matching TCP/UDP ports; default is false")
183 private boolean matchTcpUdpPorts = false;
184
185 @Property(name = "matchIcmpFields", boolValue = false,
186 label = "Enable matching ICMPv4 and ICMPv6 fields; " +
Thomas Vachuska27bee092015-06-23 19:03:10 -0700187 "default is false")
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100188 private boolean matchIcmpFields = false;
189
Sahil Lelea69534f2015-07-16 15:14:57 -0700190
Rusty Eddy29acad62015-07-07 19:33:47 -0700191 @Property(name = "ignoreIPv4Multicast", boolValue = false,
192 label = "Ignore (do not forward) IPv4 multicast packets; default is false")
193 private boolean ignoreIpv4McastPackets = false;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100194
kalagesa1a1867a2017-03-07 13:06:41 +0530195 @Property(name = "recordMetrics", boolValue = false,
196 label = "Enable record metrics for reactive forwarding")
197 private boolean recordMetrics = false;
198
Sahil Lelea69534f2015-07-16 15:14:57 -0700199 private final TopologyListener topologyListener = new InternalTopologyListener();
200
201
alshabib030111e2014-09-15 15:56:42 -0700202 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900203 public void activate(ComponentContext context) {
kalagesa1a1867a2017-03-07 13:06:41 +0530204 KryoNamespace.Builder metricSerializer = KryoNamespace.newBuilder()
205 .register(KryoNamespaces.API)
206 .register(ReactiveForwardMetrics.class)
207 .register(MultiValuedTimestamp.class);
208 metrics = storageService.<MacAddress, ReactiveForwardMetrics>eventuallyConsistentMapBuilder()
209 .withName("metrics-fwd")
210 .withSerializer(metricSerializer)
211 .withTimestampProvider((key, metricsData) -> new
212 MultiValuedTimestamp<>(new WallClockTimestamp(), System.nanoTime()))
213 .build();
214
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700215 cfgService.registerProperties(getClass());
Brian O'Connorabafb502014-12-02 22:26:20 -0800216 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800217
Brian O'Connor3b783262015-07-29 17:49:24 -0700218 packetService.addProcessor(processor, PacketProcessor.director(2));
Sahil Lelea69534f2015-07-16 15:14:57 -0700219 topologyService.addListener(topologyListener);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900220 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700221 requestIntercepts();
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800222
Thomas Vachuska8d033672015-07-21 16:15:04 -0700223 log.info("Started", appId.id());
Charles M.C. Chane148de82015-05-06 12:38:21 +0800224 }
225
226 @Deactivate
227 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800228 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700229 withdrawIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800230 flowRuleService.removeFlowRulesById(appId);
231 packetService.removeProcessor(processor);
Sahil Lelea69534f2015-07-16 15:14:57 -0700232 topologyService.removeListener(topologyListener);
Charles M.C. Chane148de82015-05-06 12:38:21 +0800233 processor = null;
234 log.info("Stopped");
235 }
236
237 @Modified
238 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800239 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700240 requestIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800241 }
242
243 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700244 * Request packet in via packet service.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800245 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700246 private void requestIntercepts() {
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800247 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
248 selector.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700249 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100250 selector.matchEthType(Ethernet.TYPE_ARP);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700251 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100252
Thomas Vachuska27bee092015-06-23 19:03:10 -0700253 selector.matchEthType(Ethernet.TYPE_IPV6);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100254 if (ipv6Forwarding) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700255 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
256 } else {
257 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100258 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900259 }
260
261 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700262 * Cancel request for packet in via packet service.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700263 */
264 private void withdrawIntercepts() {
265 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
266 selector.matchEthType(Ethernet.TYPE_IPV4);
267 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
268 selector.matchEthType(Ethernet.TYPE_ARP);
269 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
270 selector.matchEthType(Ethernet.TYPE_IPV6);
271 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
272 }
273
274 /**
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900275 * Extracts properties from the component configuration context.
276 *
277 * @param context the component context
278 */
279 private void readComponentConfiguration(ComponentContext context) {
280 Dictionary<?, ?> properties = context.getProperties();
Jian Lid9b5f552016-03-11 18:15:31 -0800281
282 Boolean packetOutOnlyEnabled =
283 Tools.isPropertyEnabled(properties, "packetOutOnly");
284 if (packetOutOnlyEnabled == null) {
285 log.info("Packet-out is not configured, " +
286 "using current value of {}", packetOutOnly);
287 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900288 packetOutOnly = packetOutOnlyEnabled;
289 log.info("Configured. Packet-out only forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800290 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700291 }
Jian Lid9b5f552016-03-11 18:15:31 -0800292
293 Boolean packetOutOfppTableEnabled =
294 Tools.isPropertyEnabled(properties, "packetOutOfppTable");
295 if (packetOutOfppTableEnabled == null) {
296 log.info("OFPP_TABLE port is not configured, " +
297 "using current value of {}", packetOutOfppTable);
298 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100299 packetOutOfppTable = packetOutOfppTableEnabled;
300 log.info("Configured. Forwarding using OFPP_TABLE port is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800301 packetOutOfppTable ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100302 }
Jian Lid9b5f552016-03-11 18:15:31 -0800303
304 Boolean ipv6ForwardingEnabled =
305 Tools.isPropertyEnabled(properties, "ipv6Forwarding");
306 if (ipv6ForwardingEnabled == null) {
307 log.info("IPv6 forwarding is not configured, " +
308 "using current value of {}", ipv6Forwarding);
309 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900310 ipv6Forwarding = ipv6ForwardingEnabled;
311 log.info("Configured. IPv6 forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800312 ipv6Forwarding ? "enabled" : "disabled");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900313 }
Jian Lid9b5f552016-03-11 18:15:31 -0800314
315 Boolean matchDstMacOnlyEnabled =
316 Tools.isPropertyEnabled(properties, "matchDstMacOnly");
317 if (matchDstMacOnlyEnabled == null) {
318 log.info("Match Dst MAC is not configured, " +
319 "using current value of {}", matchDstMacOnly);
320 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100321 matchDstMacOnly = matchDstMacOnlyEnabled;
322 log.info("Configured. Match Dst MAC Only is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800323 matchDstMacOnly ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100324 }
Jian Lid9b5f552016-03-11 18:15:31 -0800325
326 Boolean matchVlanIdEnabled =
327 Tools.isPropertyEnabled(properties, "matchVlanId");
328 if (matchVlanIdEnabled == null) {
329 log.info("Matching Vlan ID is not configured, " +
330 "using current value of {}", matchVlanId);
331 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100332 matchVlanId = matchVlanIdEnabled;
333 log.info("Configured. Matching Vlan ID is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800334 matchVlanId ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100335 }
Jian Lid9b5f552016-03-11 18:15:31 -0800336
337 Boolean matchIpv4AddressEnabled =
338 Tools.isPropertyEnabled(properties, "matchIpv4Address");
339 if (matchIpv4AddressEnabled == null) {
340 log.info("Matching IPv4 Address is not configured, " +
341 "using current value of {}", matchIpv4Address);
342 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100343 matchIpv4Address = matchIpv4AddressEnabled;
344 log.info("Configured. Matching IPv4 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800345 matchIpv4Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100346 }
Jian Lid9b5f552016-03-11 18:15:31 -0800347
348 Boolean matchIpv4DscpEnabled =
349 Tools.isPropertyEnabled(properties, "matchIpv4Dscp");
350 if (matchIpv4DscpEnabled == null) {
351 log.info("Matching IPv4 DSCP and ECN is not configured, " +
352 "using current value of {}", matchIpv4Dscp);
353 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100354 matchIpv4Dscp = matchIpv4DscpEnabled;
355 log.info("Configured. Matching IPv4 DSCP and ECN is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800356 matchIpv4Dscp ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100357 }
Jian Lid9b5f552016-03-11 18:15:31 -0800358
359 Boolean matchIpv6AddressEnabled =
360 Tools.isPropertyEnabled(properties, "matchIpv6Address");
361 if (matchIpv6AddressEnabled == null) {
362 log.info("Matching IPv6 Address is not configured, " +
363 "using current value of {}", matchIpv6Address);
364 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100365 matchIpv6Address = matchIpv6AddressEnabled;
366 log.info("Configured. Matching IPv6 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800367 matchIpv6Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100368 }
Jian Lid9b5f552016-03-11 18:15:31 -0800369
370 Boolean matchIpv6FlowLabelEnabled =
371 Tools.isPropertyEnabled(properties, "matchIpv6FlowLabel");
372 if (matchIpv6FlowLabelEnabled == null) {
373 log.info("Matching IPv6 FlowLabel is not configured, " +
374 "using current value of {}", matchIpv6FlowLabel);
375 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100376 matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
377 log.info("Configured. Matching IPv6 FlowLabel is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800378 matchIpv6FlowLabel ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100379 }
Jian Lid9b5f552016-03-11 18:15:31 -0800380
381 Boolean matchTcpUdpPortsEnabled =
382 Tools.isPropertyEnabled(properties, "matchTcpUdpPorts");
383 if (matchTcpUdpPortsEnabled == null) {
384 log.info("Matching TCP/UDP fields is not configured, " +
385 "using current value of {}", matchTcpUdpPorts);
386 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100387 matchTcpUdpPorts = matchTcpUdpPortsEnabled;
388 log.info("Configured. Matching TCP/UDP fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800389 matchTcpUdpPorts ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100390 }
Jian Lid9b5f552016-03-11 18:15:31 -0800391
392 Boolean matchIcmpFieldsEnabled =
393 Tools.isPropertyEnabled(properties, "matchIcmpFields");
394 if (matchIcmpFieldsEnabled == null) {
395 log.info("Matching ICMP (v4 and v6) fields is not configured, " +
396 "using current value of {}", matchIcmpFields);
397 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100398 matchIcmpFields = matchIcmpFieldsEnabled;
399 log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800400 matchIcmpFields ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100401 }
Rusty Eddy29acad62015-07-07 19:33:47 -0700402
Jian Lid9b5f552016-03-11 18:15:31 -0800403 Boolean ignoreIpv4McastPacketsEnabled =
404 Tools.isPropertyEnabled(properties, "ignoreIpv4McastPackets");
405 if (ignoreIpv4McastPacketsEnabled == null) {
406 log.info("Ignore IPv4 multi-cast packet is not configured, " +
407 "using current value of {}", ignoreIpv4McastPackets);
408 } else {
Rusty Eddy29acad62015-07-07 19:33:47 -0700409 ignoreIpv4McastPackets = ignoreIpv4McastPacketsEnabled;
410 log.info("Configured. Ignore IPv4 multicast packets is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800411 ignoreIpv4McastPackets ? "enabled" : "disabled");
Rusty Eddy29acad62015-07-07 19:33:47 -0700412 }
kalagesa1a1867a2017-03-07 13:06:41 +0530413 Boolean recordMetricsEnabled =
414 Tools.isPropertyEnabled(properties, "recordMetrics");
415 if (recordMetricsEnabled == null) {
416 log.info("IConfigured. Ignore record metrics is {} ," +
417 "using current value of {}", recordMetrics);
418 } else {
419 recordMetrics = recordMetricsEnabled;
420 log.info("Configured. record metrics is {}",
421 recordMetrics ? "enabled" : "disabled");
422 }
423
Jian Lid9b5f552016-03-11 18:15:31 -0800424 flowTimeout = Tools.getIntegerProperty(properties, "flowTimeout", DEFAULT_TIMEOUT);
Yoonseon Han2f011ad2016-08-08 17:06:56 -0700425 log.info("Configured. Flow Timeout is configured to {} seconds", flowTimeout);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100426
Jian Lid9b5f552016-03-11 18:15:31 -0800427 flowPriority = Tools.getIntegerProperty(properties, "flowPriority", DEFAULT_PRIORITY);
428 log.info("Configured. Flow Priority is configured to {}", flowPriority);
tomc16656f2014-10-15 18:30:31 -0700429 }
tomc370ebd2014-09-16 01:25:21 -0700430
431 /**
432 * Packet processor responsible for forwarding packets along their paths.
433 */
434 private class ReactivePacketProcessor implements PacketProcessor {
435
436 @Override
437 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700438 // Stop processing if the packet has been handled, since we
439 // can't do any more to it.
Sahil Lelea69534f2015-07-16 15:14:57 -0700440
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800441 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700442 return;
443 }
tomdc95b8a2014-09-17 08:07:26 -0700444
tomc370ebd2014-09-16 01:25:21 -0700445 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700446 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700447
Jonathan Harte8600eb2015-01-12 10:30:45 -0800448 if (ethPkt == null) {
449 return;
450 }
451
kalagesa1a1867a2017-03-07 13:06:41 +0530452 MacAddress macAddress = ethPkt.getSourceMAC();
453 ReactiveForwardMetrics macMetrics = null;
454 macMetrics = createCounter(macAddress);
455 inPacket(macMetrics);
456
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900457 // Bail if this is deemed to be a control packet.
458 if (isControlPacket(ethPkt)) {
kalagesa1a1867a2017-03-07 13:06:41 +0530459 droppedPacket(macMetrics);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900460 return;
461 }
462
463 // Skip IPv6 multicast packet when IPv6 forward is disabled.
464 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
kalagesa1a1867a2017-03-07 13:06:41 +0530465 droppedPacket(macMetrics);
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800466 return;
467 }
468
tom642b2262014-09-17 13:52:55 -0700469 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700470
Charles Chan928ff8b2017-05-04 12:22:54 -0700471 // Do not process LLDP MAC address in any way.
472 if (id.mac().isLldp()) {
kalagesa1a1867a2017-03-07 13:06:41 +0530473 droppedPacket(macMetrics);
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700474 return;
475 }
476
Rusty Eddy29acad62015-07-07 19:33:47 -0700477 // Do not process IPv4 multicast packets, let mfwd handle them
478 if (ignoreIpv4McastPackets && ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
479 if (id.mac().isMulticast()) {
480 return;
481 }
482 }
483
tomc370ebd2014-09-16 01:25:21 -0700484 // Do we know who this is for? If not, flood and bail.
485 Host dst = hostService.getHost(id);
486 if (dst == null) {
kalagesa1a1867a2017-03-07 13:06:41 +0530487 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700488 return;
489 }
490
491 // Are we on an edge switch that our destination is on? If so,
492 // simply forward out to the destination and bail.
493 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700494 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
kalagesa1a1867a2017-03-07 13:06:41 +0530495 installRule(context, dst.location().port(), macMetrics);
alshabib6eb438a2014-10-01 16:39:37 -0700496 }
tomc370ebd2014-09-16 01:25:21 -0700497 return;
498 }
499
500 // Otherwise, get a set of paths that lead from here to the
501 // destination edge switch.
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100502 Set<Path> paths =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700503 topologyService.getPaths(topologyService.currentTopology(),
504 pkt.receivedFrom().deviceId(),
505 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700506 if (paths.isEmpty()) {
507 // If there are no paths, flood and bail.
kalagesa1a1867a2017-03-07 13:06:41 +0530508 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700509 return;
510 }
511
512 // Otherwise, pick a path that does not lead back to where we
513 // came from; if no such path, flood and bail.
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700514 Path path = pickForwardPathIfPossible(paths, pkt.receivedFrom().port());
tomc370ebd2014-09-16 01:25:21 -0700515 if (path == null) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700516 log.warn("Don't know where to go from here {} for {} -> {}",
517 pkt.receivedFrom(), ethPkt.getSourceMAC(), ethPkt.getDestinationMAC());
kalagesa1a1867a2017-03-07 13:06:41 +0530518 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700519 return;
520 }
521
522 // Otherwise forward and be done with it.
kalagesa1a1867a2017-03-07 13:06:41 +0530523 installRule(context, path.src().port(), macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700524 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800525
526 }
527
528 // Indicates whether this is a control packet, e.g. LLDP, BDDP
529 private boolean isControlPacket(Ethernet eth) {
530 short type = eth.getEtherType();
531 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700532 }
533
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800534 // Indicated whether this is an IPv6 multicast packet.
535 private boolean isIpv6Multicast(Ethernet eth) {
536 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
537 }
538
tomc370ebd2014-09-16 01:25:21 -0700539 // Selects a path from the given set that does not lead back to the
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700540 // specified port if possible.
541 private Path pickForwardPathIfPossible(Set<Path> paths, PortNumber notToPort) {
542 Path lastPath = null;
tomc370ebd2014-09-16 01:25:21 -0700543 for (Path path : paths) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700544 lastPath = path;
tomc370ebd2014-09-16 01:25:21 -0700545 if (!path.src().port().equals(notToPort)) {
546 return path;
547 }
548 }
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700549 return lastPath;
tomc370ebd2014-09-16 01:25:21 -0700550 }
551
tom642b2262014-09-17 13:52:55 -0700552 // Floods the specified packet if permissible.
kalagesa1a1867a2017-03-07 13:06:41 +0530553 private void flood(PacketContext context, ReactiveForwardMetrics macMetrics) {
tomdc95b8a2014-09-17 08:07:26 -0700554 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700555 context.inPacket().receivedFrom())) {
kalagesa1a1867a2017-03-07 13:06:41 +0530556 packetOut(context, PortNumber.FLOOD, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700557 } else {
558 context.block();
559 }
560 }
561
tom642b2262014-09-17 13:52:55 -0700562 // Sends a packet out the specified port.
kalagesa1a1867a2017-03-07 13:06:41 +0530563 private void packetOut(PacketContext context, PortNumber portNumber, ReactiveForwardMetrics macMetrics) {
564 replyPacket(macMetrics);
alshabib010c31d2014-09-26 10:01:12 -0700565 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700566 context.send();
567 }
568
569 // Install a rule forwarding the packet to the specified port.
kalagesa1a1867a2017-03-07 13:06:41 +0530570 private void installRule(PacketContext context, PortNumber portNumber, ReactiveForwardMetrics macMetrics) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100571 //
572 // We don't support (yet) buffer IDs in the Flow Service so
573 // packet out first.
574 //
575 Ethernet inPkt = context.inPacket().parsed();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700576 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100577
578 // If PacketOutOnly or ARP packet than forward directly to output port
579 if (packetOutOnly || inPkt.getEtherType() == Ethernet.TYPE_ARP) {
kalagesa1a1867a2017-03-07 13:06:41 +0530580 packetOut(context, portNumber, macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100581 return;
582 }
583
584 //
585 // If matchDstMacOnly
586 // Create flows matching dstMac only
587 // Else
588 // Create flows with default matching and include configured fields
589 //
590 if (matchDstMacOnly) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700591 selectorBuilder.matchEthDst(inPkt.getDestinationMAC());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100592 } else {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700593 selectorBuilder.matchInPort(context.inPacket().receivedFrom().port())
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800594 .matchEthSrc(inPkt.getSourceMAC())
Jonathan Hart430223a2015-04-22 17:39:02 -0700595 .matchEthDst(inPkt.getDestinationMAC());
alshabib7b2748f2014-09-16 20:21:11 -0700596
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100597 // If configured Match Vlan ID
598 if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700599 selectorBuilder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100600 }
alshabib7b2748f2014-09-16 20:21:11 -0700601
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100602 //
603 // If configured and EtherType is IPv4 - Match IPv4 and
604 // TCP/UDP/ICMP fields
605 //
606 if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
607 IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
608 byte ipv4Protocol = ipv4Packet.getProtocol();
609 Ip4Prefix matchIp4SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700610 Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
611 Ip4Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100612 Ip4Prefix matchIp4DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700613 Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
614 Ip4Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800615 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
Jonathan Hart430223a2015-04-22 17:39:02 -0700616 .matchIPSrc(matchIp4SrcPrefix)
617 .matchIPDst(matchIp4DstPrefix);
alshabib6eb438a2014-10-01 16:39:37 -0700618
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100619 if (matchIpv4Dscp) {
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -0800620 byte dscp = ipv4Packet.getDscp();
621 byte ecn = ipv4Packet.getEcn();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700622 selectorBuilder.matchIPDscp(dscp).matchIPEcn(ecn);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100623 }
624
625 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
626 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700627 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700628 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
629 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100630 }
631 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
632 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700633 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700634 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
635 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100636 }
637 if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
638 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700639 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700640 .matchIcmpType(icmpPacket.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100641 .matchIcmpCode(icmpPacket.getIcmpCode());
642 }
643 }
644
645 //
646 // If configured and EtherType is IPv6 - Match IPv6 and
647 // TCP/UDP/ICMP fields
648 //
649 if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
650 IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
651 byte ipv6NextHeader = ipv6Packet.getNextHeader();
652 Ip6Prefix matchIp6SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700653 Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
654 Ip6Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100655 Ip6Prefix matchIp6DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700656 Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
657 Ip6Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800658 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6)
659 .matchIPv6Src(matchIp6SrcPrefix)
Jonathan Hart430223a2015-04-22 17:39:02 -0700660 .matchIPv6Dst(matchIp6DstPrefix);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100661
662 if (matchIpv6FlowLabel) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700663 selectorBuilder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100664 }
665
666 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
667 TCP tcpPacket = (TCP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700668 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700669 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
670 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100671 }
672 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
673 UDP udpPacket = (UDP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700674 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700675 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
676 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100677 }
678 if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
679 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700680 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700681 .matchIcmpv6Type(icmp6Packet.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100682 .matchIcmpv6Code(icmp6Packet.getIcmpCode());
683 }
684 }
685 }
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700686 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
687 .setOutput(portNumber)
688 .build();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100689
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700690 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
691 .withSelector(selectorBuilder.build())
692 .withTreatment(treatment)
693 .withPriority(flowPriority)
694 .withFlag(ForwardingObjective.Flag.VERSATILE)
695 .fromApp(appId)
696 .makeTemporary(flowTimeout)
697 .add();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100698
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700699 flowObjectiveService.forward(context.inPacket().receivedFrom().deviceId(),
700 forwardingObjective);
kalagesa1a1867a2017-03-07 13:06:41 +0530701 forwardPacket(macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100702 //
703 // If packetOutOfppTable
704 // Send packet back to the OpenFlow pipeline to match installed flow
705 // Else
706 // Send packet direction on the appropriate port
707 //
708 if (packetOutOfppTable) {
kalagesa1a1867a2017-03-07 13:06:41 +0530709 packetOut(context, PortNumber.TABLE, macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100710 } else {
kalagesa1a1867a2017-03-07 13:06:41 +0530711 packetOut(context, portNumber, macMetrics);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800712 }
tomc370ebd2014-09-16 01:25:21 -0700713 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700714
kalagesa1a1867a2017-03-07 13:06:41 +0530715
Sahil Lelea69534f2015-07-16 15:14:57 -0700716 private class InternalTopologyListener implements TopologyListener {
717 @Override
718 public void event(TopologyEvent event) {
719 List<Event> reasons = event.reasons();
720 if (reasons != null) {
721 reasons.forEach(re -> {
722 if (re instanceof LinkEvent) {
723 LinkEvent le = (LinkEvent) re;
724 if (le.type() == LinkEvent.Type.LINK_REMOVED) {
725 fixBlackhole(le.subject().src());
726 }
727 }
728 });
729 }
730 }
731 }
732
733 private void fixBlackhole(ConnectPoint egress) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700734 Set<FlowEntry> rules = getFlowRulesFrom(egress);
Sahil Lelea69534f2015-07-16 15:14:57 -0700735 Set<SrcDstPair> pairs = findSrcDstPairs(rules);
736
Sahil Lele7b961662015-07-20 15:06:04 -0700737 Map<DeviceId, Set<Path>> srcPaths = new HashMap<>();
738
Thomas Vachuska8d033672015-07-21 16:15:04 -0700739 for (SrcDstPair sd : pairs) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700740 // get the edge deviceID for the src host
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700741 Host srcHost = hostService.getHost(HostId.hostId(sd.src));
742 Host dstHost = hostService.getHost(HostId.hostId(sd.dst));
743 if (srcHost != null && dstHost != null) {
744 DeviceId srcId = srcHost.location().deviceId();
745 DeviceId dstId = dstHost.location().deviceId();
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800746 log.trace("SRC ID is {}, DST ID is {}", srcId, dstId);
Sahil Lelea69534f2015-07-16 15:14:57 -0700747
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700748 cleanFlowRules(sd, egress.deviceId());
Sahil Lelea69534f2015-07-16 15:14:57 -0700749
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700750 Set<Path> shortestPaths = srcPaths.get(srcId);
751 if (shortestPaths == null) {
752 shortestPaths = topologyService.getPaths(topologyService.currentTopology(),
753 egress.deviceId(), srcId);
754 srcPaths.put(srcId, shortestPaths);
755 }
756 backTrackBadNodes(shortestPaths, dstId, sd);
Sahil Lele7b961662015-07-20 15:06:04 -0700757 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700758 }
759 }
760
761 // Backtracks from link down event to remove flows that lead to blackhole
762 private void backTrackBadNodes(Set<Path> shortestPaths, DeviceId dstId, SrcDstPair sd) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700763 for (Path p : shortestPaths) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700764 List<Link> pathLinks = p.links();
765 for (int i = 0; i < pathLinks.size(); i = i + 1) {
766 Link curLink = pathLinks.get(i);
767 DeviceId curDevice = curLink.src().deviceId();
Sahil Lelea69534f2015-07-16 15:14:57 -0700768
769 // skipping the first link because this link's src has already been pruned beforehand
770 if (i != 0) {
771 cleanFlowRules(sd, curDevice);
772 }
773
Thomas Vachuska8d033672015-07-21 16:15:04 -0700774 Set<Path> pathsFromCurDevice =
775 topologyService.getPaths(topologyService.currentTopology(),
776 curDevice, dstId);
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700777 if (pickForwardPathIfPossible(pathsFromCurDevice, curLink.src().port()) != null) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700778 break;
779 } else {
780 if (i + 1 == pathLinks.size()) {
781 cleanFlowRules(sd, curLink.dst().deviceId());
782 }
783 }
784 }
785 }
786 }
787
788 // Removes flow rules off specified device with specific SrcDstPair
789 private void cleanFlowRules(SrcDstPair pair, DeviceId id) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800790 log.trace("Searching for flow rules to remove from: {}", id);
791 log.trace("Removing flows w/ SRC={}, DST={}", pair.src, pair.dst);
Sahil Lelea69534f2015-07-16 15:14:57 -0700792 for (FlowEntry r : flowRuleService.getFlowEntries(id)) {
793 boolean matchesSrc = false, matchesDst = false;
794 for (Instruction i : r.treatment().allInstructions()) {
795 if (i.type() == Instruction.Type.OUTPUT) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700796 // if the flow has matching src and dst
Sahil Lelea69534f2015-07-16 15:14:57 -0700797 for (Criterion cr : r.selector().criteria()) {
798 if (cr.type() == Criterion.Type.ETH_DST) {
799 if (((EthCriterion) cr).mac().equals(pair.dst)) {
800 matchesDst = true;
801 }
802 } else if (cr.type() == Criterion.Type.ETH_SRC) {
803 if (((EthCriterion) cr).mac().equals(pair.src)) {
804 matchesSrc = true;
805 }
806 }
807 }
808 }
809 }
810 if (matchesDst && matchesSrc) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800811 log.trace("Removed flow rule from device: {}", id);
Sahil Lelea69534f2015-07-16 15:14:57 -0700812 flowRuleService.removeFlowRules((FlowRule) r);
813 }
814 }
815
816 }
817
818 // Returns a set of src/dst MAC pairs extracted from the specified set of flow entries
819 private Set<SrcDstPair> findSrcDstPairs(Set<FlowEntry> rules) {
820 ImmutableSet.Builder<SrcDstPair> builder = ImmutableSet.builder();
Thomas Vachuska8d033672015-07-21 16:15:04 -0700821 for (FlowEntry r : rules) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700822 MacAddress src = null, dst = null;
Thomas Vachuska8d033672015-07-21 16:15:04 -0700823 for (Criterion cr : r.selector().criteria()) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700824 if (cr.type() == Criterion.Type.ETH_DST) {
825 dst = ((EthCriterion) cr).mac();
826 } else if (cr.type() == Criterion.Type.ETH_SRC) {
827 src = ((EthCriterion) cr).mac();
828 }
829 }
830 builder.add(new SrcDstPair(src, dst));
831 }
832 return builder.build();
833 }
834
kalagesa1a1867a2017-03-07 13:06:41 +0530835 private ReactiveForwardMetrics createCounter(MacAddress macAddress) {
836 ReactiveForwardMetrics macMetrics = null;
837 if (recordMetrics) {
838 macMetrics = metrics.compute(macAddress, (key, existingValue) -> {
839 if (existingValue == null) {
840 return new ReactiveForwardMetrics(0L, 0L, 0L, 0L, macAddress);
841 } else {
842 return existingValue;
843 }
844 });
845 }
846 return macMetrics;
847 }
848
849 private void forwardPacket(ReactiveForwardMetrics macmetrics) {
850 if (recordMetrics) {
851 macmetrics.incrementForwardedPacket();
852 metrics.put(macmetrics.getMacAddress(), macmetrics);
853 }
854 }
855
856 private void inPacket(ReactiveForwardMetrics macmetrics) {
857 if (recordMetrics) {
858 macmetrics.incrementInPacket();
859 metrics.put(macmetrics.getMacAddress(), macmetrics);
860 }
861 }
862
863 private void replyPacket(ReactiveForwardMetrics macmetrics) {
864 if (recordMetrics) {
865 macmetrics.incremnetReplyPacket();
866 metrics.put(macmetrics.getMacAddress(), macmetrics);
867 }
868 }
869
870 private void droppedPacket(ReactiveForwardMetrics macmetrics) {
871 if (recordMetrics) {
872 macmetrics.incrementDroppedPacket();
873 metrics.put(macmetrics.getMacAddress(), macmetrics);
874 }
875 }
876
877 public EventuallyConsistentMap<MacAddress, ReactiveForwardMetrics> getMacAddress() {
878 return metrics;
879 }
880
881 public void printMetric(MacAddress mac) {
882 System.out.println("-----------------------------------------------------------------------------------------");
883 System.out.println(" MACADDRESS \t\t\t\t\t\t Metrics");
884 if (mac != null) {
885 System.out.println(" " + mac + " \t\t\t " + metrics.get(mac));
886 } else {
887 for (MacAddress key : metrics.keySet()) {
888 System.out.println(" " + key + " \t\t\t " + metrics.get(key));
889 }
890 }
891 }
892
Sahil Lelea69534f2015-07-16 15:14:57 -0700893 private Set<FlowEntry> getFlowRulesFrom(ConnectPoint egress) {
894 ImmutableSet.Builder<FlowEntry> builder = ImmutableSet.builder();
895 flowRuleService.getFlowEntries(egress.deviceId()).forEach(r -> {
896 if (r.appId() == appId.id()) {
897 r.treatment().allInstructions().forEach(i -> {
898 if (i.type() == Instruction.Type.OUTPUT) {
899 if (((Instructions.OutputInstruction) i).port().equals(egress.port())) {
900 builder.add(r);
901 }
902 }
903 });
904 }
905 });
906
907 return builder.build();
908 }
909
910 // Wrapper class for a source and destination pair of MAC addresses
911 private final class SrcDstPair {
912 final MacAddress src;
913 final MacAddress dst;
914
915 private SrcDstPair(MacAddress src, MacAddress dst) {
916 this.src = src;
917 this.dst = dst;
918 }
919
920 @Override
921 public boolean equals(Object o) {
922 if (this == o) {
923 return true;
924 }
925 if (o == null || getClass() != o.getClass()) {
926 return false;
927 }
928 SrcDstPair that = (SrcDstPair) o;
929 return Objects.equals(src, that.src) &&
930 Objects.equals(dst, that.dst);
931 }
932
933 @Override
934 public int hashCode() {
935 return Objects.hash(src, dst);
936 }
937 }
alshabib030111e2014-09-15 15:56:42 -0700938}