blob: 83dc3bc23c9b6926b39a8577a0aab082d1240ac4 [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;
Jonathan Harte8600eb2015-01-12 10:30:45 -080019import org.onlab.packet.Ethernet;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010020import org.onlab.packet.ICMP;
21import org.onlab.packet.ICMP6;
Jonathan Hart430223a2015-04-22 17:39:02 -070022import org.onlab.packet.IPv4;
23import org.onlab.packet.IPv6;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010024import org.onlab.packet.Ip4Prefix;
25import org.onlab.packet.Ip6Prefix;
Sahil Lelea69534f2015-07-16 15:14:57 -070026import org.onlab.packet.MacAddress;
Jonathan Hart430223a2015-04-22 17:39:02 -070027import org.onlab.packet.TCP;
Hyunsun Mooncf732fb2015-08-22 21:04:23 -070028import org.onlab.packet.TpPort;
Jonathan Hart430223a2015-04-22 17:39:02 -070029import org.onlab.packet.UDP;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +010030import org.onlab.packet.VlanId;
kalagesa1a1867a2017-03-07 13:06:41 +053031import org.onlab.util.KryoNamespace;
maojianwei8bf77b72016-02-15 15:18:24 +080032import org.onlab.util.Tools;
Thomas Vachuska6519e6f2015-03-11 02:29:31 -070033import org.onosproject.cfg.ComponentConfigService;
Brian O'Connorabafb502014-12-02 22:26:20 -080034import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Sahil Lelea69534f2015-07-16 15:14:57 -070036import org.onosproject.event.Event;
37import org.onosproject.net.ConnectPoint;
38import org.onosproject.net.DeviceId;
Brian O'Connorabafb502014-12-02 22:26:20 -080039import org.onosproject.net.Host;
40import org.onosproject.net.HostId;
Sahil Lelea69534f2015-07-16 15:14:57 -070041import org.onosproject.net.Link;
Brian O'Connorabafb502014-12-02 22:26:20 -080042import org.onosproject.net.Path;
43import org.onosproject.net.PortNumber;
Brian O'Connorabafb502014-12-02 22:26:20 -080044import org.onosproject.net.flow.DefaultTrafficSelector;
45import org.onosproject.net.flow.DefaultTrafficTreatment;
Sahil Lelea69534f2015-07-16 15:14:57 -070046import org.onosproject.net.flow.FlowEntry;
47import org.onosproject.net.flow.FlowRule;
Brian O'Connorabafb502014-12-02 22:26:20 -080048import org.onosproject.net.flow.FlowRuleService;
49import org.onosproject.net.flow.TrafficSelector;
50import org.onosproject.net.flow.TrafficTreatment;
Sahil Lelea69534f2015-07-16 15:14:57 -070051import org.onosproject.net.flow.criteria.Criterion;
52import org.onosproject.net.flow.criteria.EthCriterion;
53import org.onosproject.net.flow.instructions.Instruction;
54import org.onosproject.net.flow.instructions.Instructions;
Jonathan Hart3b881aa2015-04-22 18:03:50 -070055import org.onosproject.net.flowobjective.DefaultForwardingObjective;
56import org.onosproject.net.flowobjective.FlowObjectiveService;
57import org.onosproject.net.flowobjective.ForwardingObjective;
Brian O'Connorabafb502014-12-02 22:26:20 -080058import org.onosproject.net.host.HostService;
Sahil Lelea69534f2015-07-16 15:14:57 -070059import org.onosproject.net.link.LinkEvent;
Brian O'Connorabafb502014-12-02 22:26:20 -080060import org.onosproject.net.packet.InboundPacket;
61import org.onosproject.net.packet.PacketContext;
Jonathan Hart3cfce8e2015-01-14 16:43:27 -080062import org.onosproject.net.packet.PacketPriority;
Brian O'Connorabafb502014-12-02 22:26:20 -080063import org.onosproject.net.packet.PacketProcessor;
64import org.onosproject.net.packet.PacketService;
Sahil Lelea69534f2015-07-16 15:14:57 -070065import org.onosproject.net.topology.TopologyEvent;
66import org.onosproject.net.topology.TopologyListener;
Brian O'Connorabafb502014-12-02 22:26:20 -080067import org.onosproject.net.topology.TopologyService;
kalagesa1a1867a2017-03-07 13:06:41 +053068import org.onosproject.store.serializers.KryoNamespaces;
69import org.onosproject.store.service.EventuallyConsistentMap;
kalagesa1a1867a2017-03-07 13:06:41 +053070import org.onosproject.store.service.MultiValuedTimestamp;
Thomas Vachuskae9d21862017-10-03 14:05:48 -070071import org.onosproject.store.service.StorageService;
72import org.onosproject.store.service.WallClockTimestamp;
73import org.osgi.service.component.ComponentContext;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070074import org.osgi.service.component.annotations.Activate;
75import org.osgi.service.component.annotations.Component;
76import org.osgi.service.component.annotations.Deactivate;
77import org.osgi.service.component.annotations.Modified;
78import org.osgi.service.component.annotations.Reference;
79import org.osgi.service.component.annotations.ReferenceCardinality;
tomc370ebd2014-09-16 01:25:21 -070080import org.slf4j.Logger;
Thomas Vachuskae9d21862017-10-03 14:05:48 -070081
Jonathan Hart430223a2015-04-22 17:39:02 -070082import java.util.Dictionary;
Sahil Lele7b961662015-07-20 15:06:04 -070083import java.util.HashMap;
Sahil Lelea69534f2015-07-16 15:14:57 -070084import java.util.List;
Sahil Lele7b961662015-07-20 15:06:04 -070085import java.util.Map;
Sahil Lelea69534f2015-07-16 15:14:57 -070086import java.util.Objects;
Jonathan Hart430223a2015-04-22 17:39:02 -070087import java.util.Set;
Thomas Vachuskae9d21862017-10-03 14:05:48 -070088import java.util.concurrent.ExecutorService;
Jonathan Hart430223a2015-04-22 17:39:02 -070089
Thomas Vachuskae9d21862017-10-03 14:05:48 -070090import static java.util.concurrent.Executors.newSingleThreadExecutor;
91import static org.onlab.util.Tools.groupedThreads;
Ray Milkey90fdd0e2018-10-30 15:27:12 -070092import static org.onosproject.fwd.OsgiPropertyConstants.FLOW_PRIORITY;
93import static org.onosproject.fwd.OsgiPropertyConstants.FLOW_PRIORITY_DEFAULT;
94import static org.onosproject.fwd.OsgiPropertyConstants.FLOW_TIMEOUT;
95import static org.onosproject.fwd.OsgiPropertyConstants.FLOW_TIMEOUT_DEFAULT;
96import static org.onosproject.fwd.OsgiPropertyConstants.IGNORE_IPV4_MCAST_PACKETS;
97import static org.onosproject.fwd.OsgiPropertyConstants.IGNORE_IPV4_MCAST_PACKETS_DEFAULT;
98import static org.onosproject.fwd.OsgiPropertyConstants.IPV6_FORWARDING;
99import static org.onosproject.fwd.OsgiPropertyConstants.IPV6_FORWARDING_DEFAULT;
100import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_DST_MAC_ONLY;
101import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_DST_MAC_ONLY_DEFAULT;
102import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_ICMP_FIELDS;
103import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_ICMP_FIELDS_DEFAULT;
104import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_IPV4_ADDRESS;
105import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_IPV4_ADDRESS_DEFAULT;
106import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_IPV4_DSCP;
107import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_IPV4_DSCP_DEFAULT;
108import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_IPV6_ADDRESS;
109import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_IPV6_ADDRESS_DEFAULT;
110import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_IPV6_FLOW_LABEL;
111import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_IPV6_FLOW_LABEL_DEFAULT;
112import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_TCP_UDP_PORTS;
113import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_TCP_UDP_PORTS_DEFAULT;
114import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_VLAN_ID;
115import static org.onosproject.fwd.OsgiPropertyConstants.MATCH_VLAN_ID_DEFAULT;
116import static org.onosproject.fwd.OsgiPropertyConstants.PACKET_OUT_OFPP_TABLE;
117import static org.onosproject.fwd.OsgiPropertyConstants.PACKET_OUT_OFPP_TABLE_DEFAULT;
118import static org.onosproject.fwd.OsgiPropertyConstants.PACKET_OUT_ONLY;
119import static org.onosproject.fwd.OsgiPropertyConstants.PACKET_OUT_ONLY_DEFAULT;
120import static org.onosproject.fwd.OsgiPropertyConstants.RECORD_METRICS;
121import static org.onosproject.fwd.OsgiPropertyConstants.RECORD_METRICS_DEFAULT;
Jonathan Hart430223a2015-04-22 17:39:02 -0700122import static org.slf4j.LoggerFactory.getLogger;
123
tomc370ebd2014-09-16 01:25:21 -0700124/**
125 * Sample reactive forwarding application.
126 */
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700127@Component(
128 immediate = true,
129 service = ReactiveForwarding.class,
130 property = {
131 PACKET_OUT_ONLY + ":Boolean=" + PACKET_OUT_ONLY_DEFAULT,
132 PACKET_OUT_OFPP_TABLE + ":Boolean=" + PACKET_OUT_OFPP_TABLE_DEFAULT,
133 FLOW_TIMEOUT + ":Integer=" + FLOW_TIMEOUT_DEFAULT,
134 FLOW_PRIORITY + ":Integer=" + FLOW_PRIORITY_DEFAULT,
135 IPV6_FORWARDING + ":Boolean=" + IPV6_FORWARDING_DEFAULT,
136 MATCH_DST_MAC_ONLY + ":Boolean=" + MATCH_DST_MAC_ONLY_DEFAULT,
137 MATCH_VLAN_ID + ":Boolean=" + MATCH_VLAN_ID_DEFAULT,
138 MATCH_IPV4_ADDRESS + ":Boolean=" + MATCH_IPV4_ADDRESS_DEFAULT,
139 MATCH_IPV4_DSCP + ":Boolean=" + MATCH_IPV4_DSCP_DEFAULT,
140 MATCH_IPV6_ADDRESS + ":Boolean=" + MATCH_IPV6_ADDRESS_DEFAULT,
141 MATCH_IPV6_FLOW_LABEL + ":Boolean=" + MATCH_IPV6_FLOW_LABEL_DEFAULT,
142 MATCH_TCP_UDP_PORTS + ":Boolean=" + MATCH_TCP_UDP_PORTS_DEFAULT,
143 MATCH_ICMP_FIELDS + ":Boolean=" + MATCH_ICMP_FIELDS_DEFAULT,
144 IGNORE_IPV4_MCAST_PACKETS + ":Boolean=" + IGNORE_IPV4_MCAST_PACKETS_DEFAULT,
145 RECORD_METRICS + ":Boolean=" + RECORD_METRICS_DEFAULT
146 }
147)
alshabib030111e2014-09-15 15:56:42 -0700148public class ReactiveForwarding {
149
tomc370ebd2014-09-16 01:25:21 -0700150 private final Logger log = getLogger(getClass());
151
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700152 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib030111e2014-09-15 15:56:42 -0700153 protected TopologyService topologyService;
154
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib030111e2014-09-15 15:56:42 -0700156 protected PacketService packetService;
157
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib8aef1ad2014-09-15 17:47:31 -0700159 protected HostService hostService;
160
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib7b2748f2014-09-16 20:21:11 -0700162 protected FlowRuleService flowRuleService;
163
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700165 protected FlowObjectiveService flowObjectiveService;
166
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib92c65ad2014-10-08 21:56:05 -0700168 protected CoreService coreService;
169
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700171 protected ComponentConfigService cfgService;
172
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
kalagesa1a1867a2017-03-07 13:06:41 +0530174 protected StorageService storageService;
175
tomc370ebd2014-09-16 01:25:21 -0700176 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -0700177
kalagesa1a1867a2017-03-07 13:06:41 +0530178 private EventuallyConsistentMap<MacAddress, ReactiveForwardMetrics> metrics;
179
alshabiba68eb962014-09-24 20:34:13 -0700180 private ApplicationId appId;
181
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700182 /** Enable packet-out only forwarding; default is false. */
183 private boolean packetOutOnly = PACKET_OUT_ONLY_DEFAULT;
tomc16656f2014-10-15 18:30:31 -0700184
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700185 /** Enable first packet forwarding using OFPP_TABLE port instead of PacketOut with actual port; default is false. */
186 private boolean packetOutOfppTable = PACKET_OUT_OFPP_TABLE_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100187
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700188 /** Configure Flow Timeout for installed flow rules; default is 10 sec. */
189 private int flowTimeout = FLOW_TIMEOUT_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100190
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700191 /** Configure Flow Priority for installed flow rules; default is 10. */
192 private int flowPriority = FLOW_PRIORITY_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100193
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700194 /** Enable IPv6 forwarding; default is false. */
195 private boolean ipv6Forwarding = IPV6_FORWARDING_DEFAULT;
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900196
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700197 /** Enable matching Dst Mac Only; default is false. */
198 private boolean matchDstMacOnly = MATCH_DST_MAC_ONLY_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100199
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700200 /** Enable matching Vlan ID; default is false. */
201 private boolean matchVlanId = MATCH_VLAN_ID_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100202
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700203 /** Enable matching IPv4 Addresses; default is false. */
204 private boolean matchIpv4Address = MATCH_IPV4_ADDRESS_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100205
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700206 /** Enable matching IPv4 DSCP and ECN; default is false. */
207 private boolean matchIpv4Dscp = MATCH_IPV4_DSCP_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100208
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700209 /** Enable matching IPv6 Addresses; default is false. */
210 private boolean matchIpv6Address = MATCH_IPV6_ADDRESS_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100211
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700212 /** Enable matching IPv6 FlowLabel; default is false. */
213 private boolean matchIpv6FlowLabel = MATCH_IPV6_FLOW_LABEL_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100214
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700215 /** Enable matching TCP/UDP ports; default is false. */
216 private boolean matchTcpUdpPorts = MATCH_TCP_UDP_PORTS_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100217
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700218 /** Enable matching ICMPv4 and ICMPv6 fields; default is false. */
219 private boolean matchIcmpFields = MATCH_ICMP_FIELDS_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100220
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700221 /** Ignore (do not forward) IPv4 multicast packets; default is false. */
222 private boolean ignoreIpv4McastPackets = IGNORE_IPV4_MCAST_PACKETS_DEFAULT;
Sahil Lelea69534f2015-07-16 15:14:57 -0700223
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700224 /** Enable record metrics for reactive forwarding. */
225 private boolean recordMetrics = RECORD_METRICS_DEFAULT;
kalagesa1a1867a2017-03-07 13:06:41 +0530226
Sahil Lelea69534f2015-07-16 15:14:57 -0700227 private final TopologyListener topologyListener = new InternalTopologyListener();
228
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700229 private ExecutorService blackHoleExecutor;
230
Sahil Lelea69534f2015-07-16 15:14:57 -0700231
alshabib030111e2014-09-15 15:56:42 -0700232 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900233 public void activate(ComponentContext context) {
kalagesa1a1867a2017-03-07 13:06:41 +0530234 KryoNamespace.Builder metricSerializer = KryoNamespace.newBuilder()
235 .register(KryoNamespaces.API)
236 .register(ReactiveForwardMetrics.class)
237 .register(MultiValuedTimestamp.class);
238 metrics = storageService.<MacAddress, ReactiveForwardMetrics>eventuallyConsistentMapBuilder()
239 .withName("metrics-fwd")
240 .withSerializer(metricSerializer)
241 .withTimestampProvider((key, metricsData) -> new
242 MultiValuedTimestamp<>(new WallClockTimestamp(), System.nanoTime()))
243 .build();
244
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700245 blackHoleExecutor = newSingleThreadExecutor(groupedThreads("onos/app/fwd",
246 "black-hole-fixer",
247 log));
248
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700249 cfgService.registerProperties(getClass());
Brian O'Connorabafb502014-12-02 22:26:20 -0800250 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800251
Brian O'Connor3b783262015-07-29 17:49:24 -0700252 packetService.addProcessor(processor, PacketProcessor.director(2));
Sahil Lelea69534f2015-07-16 15:14:57 -0700253 topologyService.addListener(topologyListener);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900254 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700255 requestIntercepts();
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800256
Thomas Vachuska8d033672015-07-21 16:15:04 -0700257 log.info("Started", appId.id());
Charles M.C. Chane148de82015-05-06 12:38:21 +0800258 }
259
260 @Deactivate
261 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800262 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700263 withdrawIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800264 flowRuleService.removeFlowRulesById(appId);
265 packetService.removeProcessor(processor);
Sahil Lelea69534f2015-07-16 15:14:57 -0700266 topologyService.removeListener(topologyListener);
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700267 blackHoleExecutor.shutdown();
268 blackHoleExecutor = null;
Charles M.C. Chane148de82015-05-06 12:38:21 +0800269 processor = null;
270 log.info("Stopped");
271 }
272
273 @Modified
274 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800275 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700276 requestIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800277 }
278
279 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700280 * Request packet in via packet service.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800281 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700282 private void requestIntercepts() {
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800283 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
284 selector.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700285 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100286
Thomas Vachuska27bee092015-06-23 19:03:10 -0700287 selector.matchEthType(Ethernet.TYPE_IPV6);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100288 if (ipv6Forwarding) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700289 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
290 } else {
291 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100292 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900293 }
294
295 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700296 * Cancel request for packet in via packet service.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700297 */
298 private void withdrawIntercepts() {
299 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
300 selector.matchEthType(Ethernet.TYPE_IPV4);
301 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700302 selector.matchEthType(Ethernet.TYPE_IPV6);
303 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
304 }
305
306 /**
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900307 * Extracts properties from the component configuration context.
308 *
309 * @param context the component context
310 */
311 private void readComponentConfiguration(ComponentContext context) {
312 Dictionary<?, ?> properties = context.getProperties();
Jian Lid9b5f552016-03-11 18:15:31 -0800313
314 Boolean packetOutOnlyEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700315 Tools.isPropertyEnabled(properties, PACKET_OUT_ONLY);
Jian Lid9b5f552016-03-11 18:15:31 -0800316 if (packetOutOnlyEnabled == null) {
317 log.info("Packet-out is not configured, " +
318 "using current value of {}", packetOutOnly);
319 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900320 packetOutOnly = packetOutOnlyEnabled;
321 log.info("Configured. Packet-out only forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800322 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700323 }
Jian Lid9b5f552016-03-11 18:15:31 -0800324
325 Boolean packetOutOfppTableEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700326 Tools.isPropertyEnabled(properties, PACKET_OUT_OFPP_TABLE);
Jian Lid9b5f552016-03-11 18:15:31 -0800327 if (packetOutOfppTableEnabled == null) {
328 log.info("OFPP_TABLE port is not configured, " +
329 "using current value of {}", packetOutOfppTable);
330 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100331 packetOutOfppTable = packetOutOfppTableEnabled;
332 log.info("Configured. Forwarding using OFPP_TABLE port is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800333 packetOutOfppTable ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100334 }
Jian Lid9b5f552016-03-11 18:15:31 -0800335
336 Boolean ipv6ForwardingEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700337 Tools.isPropertyEnabled(properties, IPV6_FORWARDING);
Jian Lid9b5f552016-03-11 18:15:31 -0800338 if (ipv6ForwardingEnabled == null) {
339 log.info("IPv6 forwarding is not configured, " +
340 "using current value of {}", ipv6Forwarding);
341 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900342 ipv6Forwarding = ipv6ForwardingEnabled;
343 log.info("Configured. IPv6 forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800344 ipv6Forwarding ? "enabled" : "disabled");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900345 }
Jian Lid9b5f552016-03-11 18:15:31 -0800346
347 Boolean matchDstMacOnlyEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700348 Tools.isPropertyEnabled(properties, MATCH_DST_MAC_ONLY);
Jian Lid9b5f552016-03-11 18:15:31 -0800349 if (matchDstMacOnlyEnabled == null) {
350 log.info("Match Dst MAC is not configured, " +
351 "using current value of {}", matchDstMacOnly);
352 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100353 matchDstMacOnly = matchDstMacOnlyEnabled;
354 log.info("Configured. Match Dst MAC Only is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800355 matchDstMacOnly ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100356 }
Jian Lid9b5f552016-03-11 18:15:31 -0800357
358 Boolean matchVlanIdEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700359 Tools.isPropertyEnabled(properties, MATCH_VLAN_ID);
Jian Lid9b5f552016-03-11 18:15:31 -0800360 if (matchVlanIdEnabled == null) {
361 log.info("Matching Vlan ID is not configured, " +
362 "using current value of {}", matchVlanId);
363 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100364 matchVlanId = matchVlanIdEnabled;
365 log.info("Configured. Matching Vlan ID is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800366 matchVlanId ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100367 }
Jian Lid9b5f552016-03-11 18:15:31 -0800368
369 Boolean matchIpv4AddressEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700370 Tools.isPropertyEnabled(properties, MATCH_IPV4_ADDRESS);
Jian Lid9b5f552016-03-11 18:15:31 -0800371 if (matchIpv4AddressEnabled == null) {
372 log.info("Matching IPv4 Address is not configured, " +
373 "using current value of {}", matchIpv4Address);
374 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100375 matchIpv4Address = matchIpv4AddressEnabled;
376 log.info("Configured. Matching IPv4 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800377 matchIpv4Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100378 }
Jian Lid9b5f552016-03-11 18:15:31 -0800379
380 Boolean matchIpv4DscpEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700381 Tools.isPropertyEnabled(properties, MATCH_IPV4_DSCP);
Jian Lid9b5f552016-03-11 18:15:31 -0800382 if (matchIpv4DscpEnabled == null) {
383 log.info("Matching IPv4 DSCP and ECN is not configured, " +
384 "using current value of {}", matchIpv4Dscp);
385 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100386 matchIpv4Dscp = matchIpv4DscpEnabled;
387 log.info("Configured. Matching IPv4 DSCP and ECN is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800388 matchIpv4Dscp ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100389 }
Jian Lid9b5f552016-03-11 18:15:31 -0800390
391 Boolean matchIpv6AddressEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700392 Tools.isPropertyEnabled(properties, MATCH_IPV6_ADDRESS);
Jian Lid9b5f552016-03-11 18:15:31 -0800393 if (matchIpv6AddressEnabled == null) {
394 log.info("Matching IPv6 Address is not configured, " +
395 "using current value of {}", matchIpv6Address);
396 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100397 matchIpv6Address = matchIpv6AddressEnabled;
398 log.info("Configured. Matching IPv6 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800399 matchIpv6Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100400 }
Jian Lid9b5f552016-03-11 18:15:31 -0800401
402 Boolean matchIpv6FlowLabelEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700403 Tools.isPropertyEnabled(properties, MATCH_IPV6_FLOW_LABEL);
Jian Lid9b5f552016-03-11 18:15:31 -0800404 if (matchIpv6FlowLabelEnabled == null) {
405 log.info("Matching IPv6 FlowLabel is not configured, " +
406 "using current value of {}", matchIpv6FlowLabel);
407 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100408 matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
409 log.info("Configured. Matching IPv6 FlowLabel is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800410 matchIpv6FlowLabel ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100411 }
Jian Lid9b5f552016-03-11 18:15:31 -0800412
413 Boolean matchTcpUdpPortsEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700414 Tools.isPropertyEnabled(properties, MATCH_TCP_UDP_PORTS);
Jian Lid9b5f552016-03-11 18:15:31 -0800415 if (matchTcpUdpPortsEnabled == null) {
416 log.info("Matching TCP/UDP fields is not configured, " +
417 "using current value of {}", matchTcpUdpPorts);
418 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100419 matchTcpUdpPorts = matchTcpUdpPortsEnabled;
420 log.info("Configured. Matching TCP/UDP fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800421 matchTcpUdpPorts ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100422 }
Jian Lid9b5f552016-03-11 18:15:31 -0800423
424 Boolean matchIcmpFieldsEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700425 Tools.isPropertyEnabled(properties, MATCH_ICMP_FIELDS);
Jian Lid9b5f552016-03-11 18:15:31 -0800426 if (matchIcmpFieldsEnabled == null) {
427 log.info("Matching ICMP (v4 and v6) fields is not configured, " +
428 "using current value of {}", matchIcmpFields);
429 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100430 matchIcmpFields = matchIcmpFieldsEnabled;
431 log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800432 matchIcmpFields ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100433 }
Rusty Eddy29acad62015-07-07 19:33:47 -0700434
Jian Lid9b5f552016-03-11 18:15:31 -0800435 Boolean ignoreIpv4McastPacketsEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700436 Tools.isPropertyEnabled(properties, IGNORE_IPV4_MCAST_PACKETS);
Jian Lid9b5f552016-03-11 18:15:31 -0800437 if (ignoreIpv4McastPacketsEnabled == null) {
438 log.info("Ignore IPv4 multi-cast packet is not configured, " +
439 "using current value of {}", ignoreIpv4McastPackets);
440 } else {
Rusty Eddy29acad62015-07-07 19:33:47 -0700441 ignoreIpv4McastPackets = ignoreIpv4McastPacketsEnabled;
442 log.info("Configured. Ignore IPv4 multicast packets is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800443 ignoreIpv4McastPackets ? "enabled" : "disabled");
Rusty Eddy29acad62015-07-07 19:33:47 -0700444 }
kalagesa1a1867a2017-03-07 13:06:41 +0530445 Boolean recordMetricsEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700446 Tools.isPropertyEnabled(properties, RECORD_METRICS);
kalagesa1a1867a2017-03-07 13:06:41 +0530447 if (recordMetricsEnabled == null) {
448 log.info("IConfigured. Ignore record metrics is {} ," +
449 "using current value of {}", recordMetrics);
450 } else {
451 recordMetrics = recordMetricsEnabled;
452 log.info("Configured. record metrics is {}",
453 recordMetrics ? "enabled" : "disabled");
454 }
455
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700456 flowTimeout = Tools.getIntegerProperty(properties, FLOW_TIMEOUT, FLOW_TIMEOUT_DEFAULT);
Yoonseon Han2f011ad2016-08-08 17:06:56 -0700457 log.info("Configured. Flow Timeout is configured to {} seconds", flowTimeout);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100458
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700459 flowPriority = Tools.getIntegerProperty(properties, FLOW_PRIORITY, FLOW_PRIORITY_DEFAULT);
Jian Lid9b5f552016-03-11 18:15:31 -0800460 log.info("Configured. Flow Priority is configured to {}", flowPriority);
tomc16656f2014-10-15 18:30:31 -0700461 }
tomc370ebd2014-09-16 01:25:21 -0700462
463 /**
464 * Packet processor responsible for forwarding packets along their paths.
465 */
466 private class ReactivePacketProcessor implements PacketProcessor {
467
468 @Override
469 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700470 // Stop processing if the packet has been handled, since we
471 // can't do any more to it.
Sahil Lelea69534f2015-07-16 15:14:57 -0700472
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800473 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700474 return;
475 }
tomdc95b8a2014-09-17 08:07:26 -0700476
tomc370ebd2014-09-16 01:25:21 -0700477 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700478 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700479
Jonathan Harte8600eb2015-01-12 10:30:45 -0800480 if (ethPkt == null) {
481 return;
482 }
483
kalagesa1a1867a2017-03-07 13:06:41 +0530484 MacAddress macAddress = ethPkt.getSourceMAC();
485 ReactiveForwardMetrics macMetrics = null;
486 macMetrics = createCounter(macAddress);
487 inPacket(macMetrics);
488
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900489 // Bail if this is deemed to be a control packet.
490 if (isControlPacket(ethPkt)) {
kalagesa1a1867a2017-03-07 13:06:41 +0530491 droppedPacket(macMetrics);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900492 return;
493 }
494
495 // Skip IPv6 multicast packet when IPv6 forward is disabled.
496 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
kalagesa1a1867a2017-03-07 13:06:41 +0530497 droppedPacket(macMetrics);
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800498 return;
499 }
500
tom642b2262014-09-17 13:52:55 -0700501 HostId id = HostId.hostId(ethPkt.getDestinationMAC());
tomc370ebd2014-09-16 01:25:21 -0700502
Charles Chan928ff8b2017-05-04 12:22:54 -0700503 // Do not process LLDP MAC address in any way.
504 if (id.mac().isLldp()) {
kalagesa1a1867a2017-03-07 13:06:41 +0530505 droppedPacket(macMetrics);
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700506 return;
507 }
508
Rusty Eddy29acad62015-07-07 19:33:47 -0700509 // Do not process IPv4 multicast packets, let mfwd handle them
510 if (ignoreIpv4McastPackets && ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
511 if (id.mac().isMulticast()) {
512 return;
513 }
514 }
515
tomc370ebd2014-09-16 01:25:21 -0700516 // Do we know who this is for? If not, flood and bail.
517 Host dst = hostService.getHost(id);
518 if (dst == null) {
kalagesa1a1867a2017-03-07 13:06:41 +0530519 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700520 return;
521 }
522
523 // Are we on an edge switch that our destination is on? If so,
524 // simply forward out to the destination and bail.
525 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700526 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
kalagesa1a1867a2017-03-07 13:06:41 +0530527 installRule(context, dst.location().port(), macMetrics);
alshabib6eb438a2014-10-01 16:39:37 -0700528 }
tomc370ebd2014-09-16 01:25:21 -0700529 return;
530 }
531
532 // Otherwise, get a set of paths that lead from here to the
533 // destination edge switch.
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100534 Set<Path> paths =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700535 topologyService.getPaths(topologyService.currentTopology(),
536 pkt.receivedFrom().deviceId(),
537 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700538 if (paths.isEmpty()) {
539 // If there are no paths, flood and bail.
kalagesa1a1867a2017-03-07 13:06:41 +0530540 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700541 return;
542 }
543
544 // Otherwise, pick a path that does not lead back to where we
545 // came from; if no such path, flood and bail.
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700546 Path path = pickForwardPathIfPossible(paths, pkt.receivedFrom().port());
tomc370ebd2014-09-16 01:25:21 -0700547 if (path == null) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700548 log.warn("Don't know where to go from here {} for {} -> {}",
549 pkt.receivedFrom(), ethPkt.getSourceMAC(), ethPkt.getDestinationMAC());
kalagesa1a1867a2017-03-07 13:06:41 +0530550 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700551 return;
552 }
553
554 // Otherwise forward and be done with it.
kalagesa1a1867a2017-03-07 13:06:41 +0530555 installRule(context, path.src().port(), macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700556 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800557
558 }
559
560 // Indicates whether this is a control packet, e.g. LLDP, BDDP
561 private boolean isControlPacket(Ethernet eth) {
562 short type = eth.getEtherType();
563 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700564 }
565
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800566 // Indicated whether this is an IPv6 multicast packet.
567 private boolean isIpv6Multicast(Ethernet eth) {
568 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
569 }
570
tomc370ebd2014-09-16 01:25:21 -0700571 // Selects a path from the given set that does not lead back to the
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700572 // specified port if possible.
573 private Path pickForwardPathIfPossible(Set<Path> paths, PortNumber notToPort) {
tomc370ebd2014-09-16 01:25:21 -0700574 for (Path path : paths) {
575 if (!path.src().port().equals(notToPort)) {
576 return path;
577 }
578 }
Cheng-An Chuangd0a70f52017-08-10 21:20:32 +0800579 return null;
tomc370ebd2014-09-16 01:25:21 -0700580 }
581
tom642b2262014-09-17 13:52:55 -0700582 // Floods the specified packet if permissible.
kalagesa1a1867a2017-03-07 13:06:41 +0530583 private void flood(PacketContext context, ReactiveForwardMetrics macMetrics) {
tomdc95b8a2014-09-17 08:07:26 -0700584 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700585 context.inPacket().receivedFrom())) {
kalagesa1a1867a2017-03-07 13:06:41 +0530586 packetOut(context, PortNumber.FLOOD, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700587 } else {
588 context.block();
589 }
590 }
591
tom642b2262014-09-17 13:52:55 -0700592 // Sends a packet out the specified port.
kalagesa1a1867a2017-03-07 13:06:41 +0530593 private void packetOut(PacketContext context, PortNumber portNumber, ReactiveForwardMetrics macMetrics) {
594 replyPacket(macMetrics);
alshabib010c31d2014-09-26 10:01:12 -0700595 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700596 context.send();
597 }
598
599 // Install a rule forwarding the packet to the specified port.
kalagesa1a1867a2017-03-07 13:06:41 +0530600 private void installRule(PacketContext context, PortNumber portNumber, ReactiveForwardMetrics macMetrics) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100601 //
602 // We don't support (yet) buffer IDs in the Flow Service so
603 // packet out first.
604 //
605 Ethernet inPkt = context.inPacket().parsed();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700606 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100607
608 // If PacketOutOnly or ARP packet than forward directly to output port
609 if (packetOutOnly || inPkt.getEtherType() == Ethernet.TYPE_ARP) {
kalagesa1a1867a2017-03-07 13:06:41 +0530610 packetOut(context, portNumber, macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100611 return;
612 }
613
614 //
615 // If matchDstMacOnly
616 // Create flows matching dstMac only
617 // Else
618 // Create flows with default matching and include configured fields
619 //
620 if (matchDstMacOnly) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700621 selectorBuilder.matchEthDst(inPkt.getDestinationMAC());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100622 } else {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700623 selectorBuilder.matchInPort(context.inPacket().receivedFrom().port())
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800624 .matchEthSrc(inPkt.getSourceMAC())
Jonathan Hart430223a2015-04-22 17:39:02 -0700625 .matchEthDst(inPkt.getDestinationMAC());
alshabib7b2748f2014-09-16 20:21:11 -0700626
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100627 // If configured Match Vlan ID
628 if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700629 selectorBuilder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100630 }
alshabib7b2748f2014-09-16 20:21:11 -0700631
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100632 //
633 // If configured and EtherType is IPv4 - Match IPv4 and
634 // TCP/UDP/ICMP fields
635 //
636 if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
637 IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
638 byte ipv4Protocol = ipv4Packet.getProtocol();
639 Ip4Prefix matchIp4SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700640 Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
641 Ip4Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100642 Ip4Prefix matchIp4DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700643 Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
644 Ip4Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800645 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
Jonathan Hart430223a2015-04-22 17:39:02 -0700646 .matchIPSrc(matchIp4SrcPrefix)
647 .matchIPDst(matchIp4DstPrefix);
alshabib6eb438a2014-10-01 16:39:37 -0700648
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100649 if (matchIpv4Dscp) {
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -0800650 byte dscp = ipv4Packet.getDscp();
651 byte ecn = ipv4Packet.getEcn();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700652 selectorBuilder.matchIPDscp(dscp).matchIPEcn(ecn);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100653 }
654
655 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
656 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700657 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700658 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
659 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100660 }
661 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
662 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700663 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700664 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
665 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100666 }
667 if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
668 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700669 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700670 .matchIcmpType(icmpPacket.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100671 .matchIcmpCode(icmpPacket.getIcmpCode());
672 }
673 }
674
675 //
676 // If configured and EtherType is IPv6 - Match IPv6 and
677 // TCP/UDP/ICMP fields
678 //
679 if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
680 IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
681 byte ipv6NextHeader = ipv6Packet.getNextHeader();
682 Ip6Prefix matchIp6SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700683 Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
684 Ip6Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100685 Ip6Prefix matchIp6DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700686 Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
687 Ip6Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800688 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6)
689 .matchIPv6Src(matchIp6SrcPrefix)
Jonathan Hart430223a2015-04-22 17:39:02 -0700690 .matchIPv6Dst(matchIp6DstPrefix);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100691
692 if (matchIpv6FlowLabel) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700693 selectorBuilder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100694 }
695
696 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
697 TCP tcpPacket = (TCP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700698 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700699 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
700 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100701 }
702 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
703 UDP udpPacket = (UDP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700704 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700705 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
706 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100707 }
708 if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
709 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700710 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700711 .matchIcmpv6Type(icmp6Packet.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100712 .matchIcmpv6Code(icmp6Packet.getIcmpCode());
713 }
714 }
715 }
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700716 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
717 .setOutput(portNumber)
718 .build();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100719
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700720 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
721 .withSelector(selectorBuilder.build())
722 .withTreatment(treatment)
723 .withPriority(flowPriority)
724 .withFlag(ForwardingObjective.Flag.VERSATILE)
725 .fromApp(appId)
726 .makeTemporary(flowTimeout)
727 .add();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100728
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700729 flowObjectiveService.forward(context.inPacket().receivedFrom().deviceId(),
730 forwardingObjective);
kalagesa1a1867a2017-03-07 13:06:41 +0530731 forwardPacket(macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100732 //
733 // If packetOutOfppTable
734 // Send packet back to the OpenFlow pipeline to match installed flow
735 // Else
736 // Send packet direction on the appropriate port
737 //
738 if (packetOutOfppTable) {
kalagesa1a1867a2017-03-07 13:06:41 +0530739 packetOut(context, PortNumber.TABLE, macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100740 } else {
kalagesa1a1867a2017-03-07 13:06:41 +0530741 packetOut(context, portNumber, macMetrics);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800742 }
tomc370ebd2014-09-16 01:25:21 -0700743 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700744
kalagesa1a1867a2017-03-07 13:06:41 +0530745
Sahil Lelea69534f2015-07-16 15:14:57 -0700746 private class InternalTopologyListener implements TopologyListener {
747 @Override
748 public void event(TopologyEvent event) {
749 List<Event> reasons = event.reasons();
750 if (reasons != null) {
751 reasons.forEach(re -> {
752 if (re instanceof LinkEvent) {
753 LinkEvent le = (LinkEvent) re;
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700754 if (le.type() == LinkEvent.Type.LINK_REMOVED && blackHoleExecutor != null) {
755 blackHoleExecutor.submit(() -> fixBlackhole(le.subject().src()));
Sahil Lelea69534f2015-07-16 15:14:57 -0700756 }
757 }
758 });
759 }
760 }
761 }
762
763 private void fixBlackhole(ConnectPoint egress) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700764 Set<FlowEntry> rules = getFlowRulesFrom(egress);
Sahil Lelea69534f2015-07-16 15:14:57 -0700765 Set<SrcDstPair> pairs = findSrcDstPairs(rules);
766
Sahil Lele7b961662015-07-20 15:06:04 -0700767 Map<DeviceId, Set<Path>> srcPaths = new HashMap<>();
768
Thomas Vachuska8d033672015-07-21 16:15:04 -0700769 for (SrcDstPair sd : pairs) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700770 // get the edge deviceID for the src host
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700771 Host srcHost = hostService.getHost(HostId.hostId(sd.src));
772 Host dstHost = hostService.getHost(HostId.hostId(sd.dst));
773 if (srcHost != null && dstHost != null) {
774 DeviceId srcId = srcHost.location().deviceId();
775 DeviceId dstId = dstHost.location().deviceId();
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800776 log.trace("SRC ID is {}, DST ID is {}", srcId, dstId);
Sahil Lelea69534f2015-07-16 15:14:57 -0700777
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700778 cleanFlowRules(sd, egress.deviceId());
Sahil Lelea69534f2015-07-16 15:14:57 -0700779
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700780 Set<Path> shortestPaths = srcPaths.get(srcId);
781 if (shortestPaths == null) {
782 shortestPaths = topologyService.getPaths(topologyService.currentTopology(),
783 egress.deviceId(), srcId);
784 srcPaths.put(srcId, shortestPaths);
785 }
786 backTrackBadNodes(shortestPaths, dstId, sd);
Sahil Lele7b961662015-07-20 15:06:04 -0700787 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700788 }
789 }
790
791 // Backtracks from link down event to remove flows that lead to blackhole
792 private void backTrackBadNodes(Set<Path> shortestPaths, DeviceId dstId, SrcDstPair sd) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700793 for (Path p : shortestPaths) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700794 List<Link> pathLinks = p.links();
795 for (int i = 0; i < pathLinks.size(); i = i + 1) {
796 Link curLink = pathLinks.get(i);
797 DeviceId curDevice = curLink.src().deviceId();
Sahil Lelea69534f2015-07-16 15:14:57 -0700798
799 // skipping the first link because this link's src has already been pruned beforehand
800 if (i != 0) {
801 cleanFlowRules(sd, curDevice);
802 }
803
Thomas Vachuska8d033672015-07-21 16:15:04 -0700804 Set<Path> pathsFromCurDevice =
805 topologyService.getPaths(topologyService.currentTopology(),
806 curDevice, dstId);
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700807 if (pickForwardPathIfPossible(pathsFromCurDevice, curLink.src().port()) != null) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700808 break;
809 } else {
810 if (i + 1 == pathLinks.size()) {
811 cleanFlowRules(sd, curLink.dst().deviceId());
812 }
813 }
814 }
815 }
816 }
817
818 // Removes flow rules off specified device with specific SrcDstPair
819 private void cleanFlowRules(SrcDstPair pair, DeviceId id) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800820 log.trace("Searching for flow rules to remove from: {}", id);
821 log.trace("Removing flows w/ SRC={}, DST={}", pair.src, pair.dst);
Sahil Lelea69534f2015-07-16 15:14:57 -0700822 for (FlowEntry r : flowRuleService.getFlowEntries(id)) {
823 boolean matchesSrc = false, matchesDst = false;
824 for (Instruction i : r.treatment().allInstructions()) {
825 if (i.type() == Instruction.Type.OUTPUT) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700826 // if the flow has matching src and dst
Sahil Lelea69534f2015-07-16 15:14:57 -0700827 for (Criterion cr : r.selector().criteria()) {
828 if (cr.type() == Criterion.Type.ETH_DST) {
829 if (((EthCriterion) cr).mac().equals(pair.dst)) {
830 matchesDst = true;
831 }
832 } else if (cr.type() == Criterion.Type.ETH_SRC) {
833 if (((EthCriterion) cr).mac().equals(pair.src)) {
834 matchesSrc = true;
835 }
836 }
837 }
838 }
839 }
840 if (matchesDst && matchesSrc) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800841 log.trace("Removed flow rule from device: {}", id);
Sahil Lelea69534f2015-07-16 15:14:57 -0700842 flowRuleService.removeFlowRules((FlowRule) r);
843 }
844 }
845
846 }
847
848 // Returns a set of src/dst MAC pairs extracted from the specified set of flow entries
849 private Set<SrcDstPair> findSrcDstPairs(Set<FlowEntry> rules) {
850 ImmutableSet.Builder<SrcDstPair> builder = ImmutableSet.builder();
Thomas Vachuska8d033672015-07-21 16:15:04 -0700851 for (FlowEntry r : rules) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700852 MacAddress src = null, dst = null;
Thomas Vachuska8d033672015-07-21 16:15:04 -0700853 for (Criterion cr : r.selector().criteria()) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700854 if (cr.type() == Criterion.Type.ETH_DST) {
855 dst = ((EthCriterion) cr).mac();
856 } else if (cr.type() == Criterion.Type.ETH_SRC) {
857 src = ((EthCriterion) cr).mac();
858 }
859 }
860 builder.add(new SrcDstPair(src, dst));
861 }
862 return builder.build();
863 }
864
kalagesa1a1867a2017-03-07 13:06:41 +0530865 private ReactiveForwardMetrics createCounter(MacAddress macAddress) {
866 ReactiveForwardMetrics macMetrics = null;
867 if (recordMetrics) {
868 macMetrics = metrics.compute(macAddress, (key, existingValue) -> {
869 if (existingValue == null) {
870 return new ReactiveForwardMetrics(0L, 0L, 0L, 0L, macAddress);
871 } else {
872 return existingValue;
873 }
874 });
875 }
876 return macMetrics;
877 }
878
879 private void forwardPacket(ReactiveForwardMetrics macmetrics) {
880 if (recordMetrics) {
881 macmetrics.incrementForwardedPacket();
882 metrics.put(macmetrics.getMacAddress(), macmetrics);
883 }
884 }
885
886 private void inPacket(ReactiveForwardMetrics macmetrics) {
887 if (recordMetrics) {
888 macmetrics.incrementInPacket();
889 metrics.put(macmetrics.getMacAddress(), macmetrics);
890 }
891 }
892
893 private void replyPacket(ReactiveForwardMetrics macmetrics) {
894 if (recordMetrics) {
895 macmetrics.incremnetReplyPacket();
896 metrics.put(macmetrics.getMacAddress(), macmetrics);
897 }
898 }
899
900 private void droppedPacket(ReactiveForwardMetrics macmetrics) {
901 if (recordMetrics) {
902 macmetrics.incrementDroppedPacket();
903 metrics.put(macmetrics.getMacAddress(), macmetrics);
904 }
905 }
906
907 public EventuallyConsistentMap<MacAddress, ReactiveForwardMetrics> getMacAddress() {
908 return metrics;
909 }
910
911 public void printMetric(MacAddress mac) {
912 System.out.println("-----------------------------------------------------------------------------------------");
913 System.out.println(" MACADDRESS \t\t\t\t\t\t Metrics");
914 if (mac != null) {
915 System.out.println(" " + mac + " \t\t\t " + metrics.get(mac));
916 } else {
917 for (MacAddress key : metrics.keySet()) {
918 System.out.println(" " + key + " \t\t\t " + metrics.get(key));
919 }
920 }
921 }
922
Sahil Lelea69534f2015-07-16 15:14:57 -0700923 private Set<FlowEntry> getFlowRulesFrom(ConnectPoint egress) {
924 ImmutableSet.Builder<FlowEntry> builder = ImmutableSet.builder();
925 flowRuleService.getFlowEntries(egress.deviceId()).forEach(r -> {
926 if (r.appId() == appId.id()) {
927 r.treatment().allInstructions().forEach(i -> {
928 if (i.type() == Instruction.Type.OUTPUT) {
929 if (((Instructions.OutputInstruction) i).port().equals(egress.port())) {
930 builder.add(r);
931 }
932 }
933 });
934 }
935 });
936
937 return builder.build();
938 }
939
940 // Wrapper class for a source and destination pair of MAC addresses
941 private final class SrcDstPair {
942 final MacAddress src;
943 final MacAddress dst;
944
945 private SrcDstPair(MacAddress src, MacAddress dst) {
946 this.src = src;
947 this.dst = dst;
948 }
949
950 @Override
951 public boolean equals(Object o) {
952 if (this == o) {
953 return true;
954 }
955 if (o == null || getClass() != o.getClass()) {
956 return false;
957 }
958 SrcDstPair that = (SrcDstPair) o;
959 return Objects.equals(src, that.src) &&
960 Objects.equals(dst, that.dst);
961 }
962
963 @Override
964 public int hashCode() {
965 return Objects.hash(src, dst);
966 }
967 }
alshabib030111e2014-09-15 15:56:42 -0700968}