blob: 61fdd3bde5c9edf1ca7860710d5ac784df9eba8e [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;
souvikdas95ce95eb72020-09-18 16:19:06 -0500122import static org.onosproject.fwd.OsgiPropertyConstants.INHERIT_FLOW_TREATMENT;
123import static org.onosproject.fwd.OsgiPropertyConstants.INHERIT_FLOW_TREATMENT_DEFAULT;
Jonathan Hart430223a2015-04-22 17:39:02 -0700124import static org.slf4j.LoggerFactory.getLogger;
125
tomc370ebd2014-09-16 01:25:21 -0700126/**
127 * Sample reactive forwarding application.
128 */
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700129@Component(
130 immediate = true,
131 service = ReactiveForwarding.class,
132 property = {
133 PACKET_OUT_ONLY + ":Boolean=" + PACKET_OUT_ONLY_DEFAULT,
134 PACKET_OUT_OFPP_TABLE + ":Boolean=" + PACKET_OUT_OFPP_TABLE_DEFAULT,
135 FLOW_TIMEOUT + ":Integer=" + FLOW_TIMEOUT_DEFAULT,
136 FLOW_PRIORITY + ":Integer=" + FLOW_PRIORITY_DEFAULT,
137 IPV6_FORWARDING + ":Boolean=" + IPV6_FORWARDING_DEFAULT,
138 MATCH_DST_MAC_ONLY + ":Boolean=" + MATCH_DST_MAC_ONLY_DEFAULT,
139 MATCH_VLAN_ID + ":Boolean=" + MATCH_VLAN_ID_DEFAULT,
140 MATCH_IPV4_ADDRESS + ":Boolean=" + MATCH_IPV4_ADDRESS_DEFAULT,
141 MATCH_IPV4_DSCP + ":Boolean=" + MATCH_IPV4_DSCP_DEFAULT,
142 MATCH_IPV6_ADDRESS + ":Boolean=" + MATCH_IPV6_ADDRESS_DEFAULT,
143 MATCH_IPV6_FLOW_LABEL + ":Boolean=" + MATCH_IPV6_FLOW_LABEL_DEFAULT,
144 MATCH_TCP_UDP_PORTS + ":Boolean=" + MATCH_TCP_UDP_PORTS_DEFAULT,
145 MATCH_ICMP_FIELDS + ":Boolean=" + MATCH_ICMP_FIELDS_DEFAULT,
146 IGNORE_IPV4_MCAST_PACKETS + ":Boolean=" + IGNORE_IPV4_MCAST_PACKETS_DEFAULT,
souvikdas95ce95eb72020-09-18 16:19:06 -0500147 RECORD_METRICS + ":Boolean=" + RECORD_METRICS_DEFAULT,
148 INHERIT_FLOW_TREATMENT + ":Boolean=" + INHERIT_FLOW_TREATMENT_DEFAULT
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700149 }
150)
alshabib030111e2014-09-15 15:56:42 -0700151public class ReactiveForwarding {
152
tomc370ebd2014-09-16 01:25:21 -0700153 private final Logger log = getLogger(getClass());
154
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700155 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib030111e2014-09-15 15:56:42 -0700156 protected TopologyService topologyService;
157
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700158 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib030111e2014-09-15 15:56:42 -0700159 protected PacketService packetService;
160
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700161 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib8aef1ad2014-09-15 17:47:31 -0700162 protected HostService hostService;
163
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700164 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib7b2748f2014-09-16 20:21:11 -0700165 protected FlowRuleService flowRuleService;
166
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700167 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700168 protected FlowObjectiveService flowObjectiveService;
169
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700170 @Reference(cardinality = ReferenceCardinality.MANDATORY)
alshabib92c65ad2014-10-08 21:56:05 -0700171 protected CoreService coreService;
172
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700173 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700174 protected ComponentConfigService cfgService;
175
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700176 @Reference(cardinality = ReferenceCardinality.MANDATORY)
kalagesa1a1867a2017-03-07 13:06:41 +0530177 protected StorageService storageService;
178
tomc370ebd2014-09-16 01:25:21 -0700179 private ReactivePacketProcessor processor = new ReactivePacketProcessor();
alshabib030111e2014-09-15 15:56:42 -0700180
kalagesa1a1867a2017-03-07 13:06:41 +0530181 private EventuallyConsistentMap<MacAddress, ReactiveForwardMetrics> metrics;
182
alshabiba68eb962014-09-24 20:34:13 -0700183 private ApplicationId appId;
184
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700185 /** Enable packet-out only forwarding; default is false. */
186 private boolean packetOutOnly = PACKET_OUT_ONLY_DEFAULT;
tomc16656f2014-10-15 18:30:31 -0700187
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700188 /** Enable first packet forwarding using OFPP_TABLE port instead of PacketOut with actual port; default is false. */
189 private boolean packetOutOfppTable = PACKET_OUT_OFPP_TABLE_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100190
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700191 /** Configure Flow Timeout for installed flow rules; default is 10 sec. */
192 private int flowTimeout = FLOW_TIMEOUT_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100193
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700194 /** Configure Flow Priority for installed flow rules; default is 10. */
195 private int flowPriority = FLOW_PRIORITY_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100196
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700197 /** Enable IPv6 forwarding; default is false. */
198 private boolean ipv6Forwarding = IPV6_FORWARDING_DEFAULT;
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900199
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700200 /** Enable matching Dst Mac Only; default is false. */
201 private boolean matchDstMacOnly = MATCH_DST_MAC_ONLY_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100202
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700203 /** Enable matching Vlan ID; default is false. */
204 private boolean matchVlanId = MATCH_VLAN_ID_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100205
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700206 /** Enable matching IPv4 Addresses; default is false. */
207 private boolean matchIpv4Address = MATCH_IPV4_ADDRESS_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100208
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700209 /** Enable matching IPv4 DSCP and ECN; default is false. */
210 private boolean matchIpv4Dscp = MATCH_IPV4_DSCP_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100211
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700212 /** Enable matching IPv6 Addresses; default is false. */
213 private boolean matchIpv6Address = MATCH_IPV6_ADDRESS_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100214
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700215 /** Enable matching IPv6 FlowLabel; default is false. */
216 private boolean matchIpv6FlowLabel = MATCH_IPV6_FLOW_LABEL_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100217
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700218 /** Enable matching TCP/UDP ports; default is false. */
219 private boolean matchTcpUdpPorts = MATCH_TCP_UDP_PORTS_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100220
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700221 /** Enable matching ICMPv4 and ICMPv6 fields; default is false. */
222 private boolean matchIcmpFields = MATCH_ICMP_FIELDS_DEFAULT;
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100223
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700224 /** Ignore (do not forward) IPv4 multicast packets; default is false. */
Ray Milkeybd508ed2019-03-19 14:22:02 -0700225 private boolean ignoreIPv4Multicast = IGNORE_IPV4_MCAST_PACKETS_DEFAULT;
Sahil Lelea69534f2015-07-16 15:14:57 -0700226
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700227 /** Enable record metrics for reactive forwarding. */
228 private boolean recordMetrics = RECORD_METRICS_DEFAULT;
kalagesa1a1867a2017-03-07 13:06:41 +0530229
souvikdas95ce95eb72020-09-18 16:19:06 -0500230 /** Enable use of builder from packet context to define flow treatment; default is false. */
231 private boolean inheritFlowTreatment = INHERIT_FLOW_TREATMENT_DEFAULT;
232
Sahil Lelea69534f2015-07-16 15:14:57 -0700233 private final TopologyListener topologyListener = new InternalTopologyListener();
234
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700235 private ExecutorService blackHoleExecutor;
236
Sahil Lelea69534f2015-07-16 15:14:57 -0700237
alshabib030111e2014-09-15 15:56:42 -0700238 @Activate
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900239 public void activate(ComponentContext context) {
kalagesa1a1867a2017-03-07 13:06:41 +0530240 KryoNamespace.Builder metricSerializer = KryoNamespace.newBuilder()
241 .register(KryoNamespaces.API)
242 .register(ReactiveForwardMetrics.class)
243 .register(MultiValuedTimestamp.class);
244 metrics = storageService.<MacAddress, ReactiveForwardMetrics>eventuallyConsistentMapBuilder()
245 .withName("metrics-fwd")
246 .withSerializer(metricSerializer)
247 .withTimestampProvider((key, metricsData) -> new
248 MultiValuedTimestamp<>(new WallClockTimestamp(), System.nanoTime()))
249 .build();
250
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700251 blackHoleExecutor = newSingleThreadExecutor(groupedThreads("onos/app/fwd",
252 "black-hole-fixer",
253 log));
254
Thomas Vachuska6519e6f2015-03-11 02:29:31 -0700255 cfgService.registerProperties(getClass());
Brian O'Connorabafb502014-12-02 22:26:20 -0800256 appId = coreService.registerApplication("org.onosproject.fwd");
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800257
Brian O'Connor3b783262015-07-29 17:49:24 -0700258 packetService.addProcessor(processor, PacketProcessor.director(2));
Sahil Lelea69534f2015-07-16 15:14:57 -0700259 topologyService.addListener(topologyListener);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900260 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700261 requestIntercepts();
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800262
Thomas Vachuska8d033672015-07-21 16:15:04 -0700263 log.info("Started", appId.id());
Charles M.C. Chane148de82015-05-06 12:38:21 +0800264 }
265
266 @Deactivate
267 public void deactivate() {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800268 cfgService.unregisterProperties(getClass(), false);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700269 withdrawIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800270 flowRuleService.removeFlowRulesById(appId);
271 packetService.removeProcessor(processor);
Sahil Lelea69534f2015-07-16 15:14:57 -0700272 topologyService.removeListener(topologyListener);
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700273 blackHoleExecutor.shutdown();
274 blackHoleExecutor = null;
Charles M.C. Chane148de82015-05-06 12:38:21 +0800275 processor = null;
276 log.info("Stopped");
277 }
278
279 @Modified
280 public void modified(ComponentContext context) {
Charles M.C. Chane148de82015-05-06 12:38:21 +0800281 readComponentConfiguration(context);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700282 requestIntercepts();
Charles M.C. Chane148de82015-05-06 12:38:21 +0800283 }
284
285 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700286 * Request packet in via packet service.
Charles M.C. Chane148de82015-05-06 12:38:21 +0800287 */
Thomas Vachuska27bee092015-06-23 19:03:10 -0700288 private void requestIntercepts() {
Jonathan Hart3cfce8e2015-01-14 16:43:27 -0800289 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
290 selector.matchEthType(Ethernet.TYPE_IPV4);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700291 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100292
Thomas Vachuska27bee092015-06-23 19:03:10 -0700293 selector.matchEthType(Ethernet.TYPE_IPV6);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100294 if (ipv6Forwarding) {
Thomas Vachuska27bee092015-06-23 19:03:10 -0700295 packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId);
296 } else {
297 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100298 }
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900299 }
300
301 /**
Thomas Vachuska8d033672015-07-21 16:15:04 -0700302 * Cancel request for packet in via packet service.
Thomas Vachuska27bee092015-06-23 19:03:10 -0700303 */
304 private void withdrawIntercepts() {
305 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
306 selector.matchEthType(Ethernet.TYPE_IPV4);
307 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
Thomas Vachuska27bee092015-06-23 19:03:10 -0700308 selector.matchEthType(Ethernet.TYPE_IPV6);
309 packetService.cancelPackets(selector.build(), PacketPriority.REACTIVE, appId);
310 }
311
312 /**
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900313 * Extracts properties from the component configuration context.
314 *
315 * @param context the component context
316 */
317 private void readComponentConfiguration(ComponentContext context) {
318 Dictionary<?, ?> properties = context.getProperties();
Jian Lid9b5f552016-03-11 18:15:31 -0800319
320 Boolean packetOutOnlyEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700321 Tools.isPropertyEnabled(properties, PACKET_OUT_ONLY);
Jian Lid9b5f552016-03-11 18:15:31 -0800322 if (packetOutOnlyEnabled == null) {
323 log.info("Packet-out is not configured, " +
324 "using current value of {}", packetOutOnly);
325 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900326 packetOutOnly = packetOutOnlyEnabled;
327 log.info("Configured. Packet-out only forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800328 packetOutOnly ? "enabled" : "disabled");
tomc16656f2014-10-15 18:30:31 -0700329 }
Jian Lid9b5f552016-03-11 18:15:31 -0800330
331 Boolean packetOutOfppTableEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700332 Tools.isPropertyEnabled(properties, PACKET_OUT_OFPP_TABLE);
Jian Lid9b5f552016-03-11 18:15:31 -0800333 if (packetOutOfppTableEnabled == null) {
334 log.info("OFPP_TABLE port is not configured, " +
335 "using current value of {}", packetOutOfppTable);
336 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100337 packetOutOfppTable = packetOutOfppTableEnabled;
338 log.info("Configured. Forwarding using OFPP_TABLE port is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800339 packetOutOfppTable ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100340 }
Jian Lid9b5f552016-03-11 18:15:31 -0800341
342 Boolean ipv6ForwardingEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700343 Tools.isPropertyEnabled(properties, IPV6_FORWARDING);
Jian Lid9b5f552016-03-11 18:15:31 -0800344 if (ipv6ForwardingEnabled == null) {
345 log.info("IPv6 forwarding is not configured, " +
346 "using current value of {}", ipv6Forwarding);
347 } else {
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900348 ipv6Forwarding = ipv6ForwardingEnabled;
349 log.info("Configured. IPv6 forwarding is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800350 ipv6Forwarding ? "enabled" : "disabled");
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900351 }
Jian Lid9b5f552016-03-11 18:15:31 -0800352
353 Boolean matchDstMacOnlyEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700354 Tools.isPropertyEnabled(properties, MATCH_DST_MAC_ONLY);
Jian Lid9b5f552016-03-11 18:15:31 -0800355 if (matchDstMacOnlyEnabled == null) {
356 log.info("Match Dst MAC is not configured, " +
357 "using current value of {}", matchDstMacOnly);
358 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100359 matchDstMacOnly = matchDstMacOnlyEnabled;
360 log.info("Configured. Match Dst MAC Only is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800361 matchDstMacOnly ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100362 }
Jian Lid9b5f552016-03-11 18:15:31 -0800363
364 Boolean matchVlanIdEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700365 Tools.isPropertyEnabled(properties, MATCH_VLAN_ID);
Jian Lid9b5f552016-03-11 18:15:31 -0800366 if (matchVlanIdEnabled == null) {
367 log.info("Matching Vlan ID is not configured, " +
368 "using current value of {}", matchVlanId);
369 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100370 matchVlanId = matchVlanIdEnabled;
371 log.info("Configured. Matching Vlan ID is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800372 matchVlanId ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100373 }
Jian Lid9b5f552016-03-11 18:15:31 -0800374
375 Boolean matchIpv4AddressEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700376 Tools.isPropertyEnabled(properties, MATCH_IPV4_ADDRESS);
Jian Lid9b5f552016-03-11 18:15:31 -0800377 if (matchIpv4AddressEnabled == null) {
378 log.info("Matching IPv4 Address is not configured, " +
379 "using current value of {}", matchIpv4Address);
380 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100381 matchIpv4Address = matchIpv4AddressEnabled;
382 log.info("Configured. Matching IPv4 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800383 matchIpv4Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100384 }
Jian Lid9b5f552016-03-11 18:15:31 -0800385
386 Boolean matchIpv4DscpEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700387 Tools.isPropertyEnabled(properties, MATCH_IPV4_DSCP);
Jian Lid9b5f552016-03-11 18:15:31 -0800388 if (matchIpv4DscpEnabled == null) {
389 log.info("Matching IPv4 DSCP and ECN is not configured, " +
390 "using current value of {}", matchIpv4Dscp);
391 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100392 matchIpv4Dscp = matchIpv4DscpEnabled;
393 log.info("Configured. Matching IPv4 DSCP and ECN is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800394 matchIpv4Dscp ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100395 }
Jian Lid9b5f552016-03-11 18:15:31 -0800396
397 Boolean matchIpv6AddressEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700398 Tools.isPropertyEnabled(properties, MATCH_IPV6_ADDRESS);
Jian Lid9b5f552016-03-11 18:15:31 -0800399 if (matchIpv6AddressEnabled == null) {
400 log.info("Matching IPv6 Address is not configured, " +
401 "using current value of {}", matchIpv6Address);
402 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100403 matchIpv6Address = matchIpv6AddressEnabled;
404 log.info("Configured. Matching IPv6 Addresses is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800405 matchIpv6Address ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100406 }
Jian Lid9b5f552016-03-11 18:15:31 -0800407
408 Boolean matchIpv6FlowLabelEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700409 Tools.isPropertyEnabled(properties, MATCH_IPV6_FLOW_LABEL);
Jian Lid9b5f552016-03-11 18:15:31 -0800410 if (matchIpv6FlowLabelEnabled == null) {
411 log.info("Matching IPv6 FlowLabel is not configured, " +
412 "using current value of {}", matchIpv6FlowLabel);
413 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100414 matchIpv6FlowLabel = matchIpv6FlowLabelEnabled;
415 log.info("Configured. Matching IPv6 FlowLabel is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800416 matchIpv6FlowLabel ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100417 }
Jian Lid9b5f552016-03-11 18:15:31 -0800418
419 Boolean matchTcpUdpPortsEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700420 Tools.isPropertyEnabled(properties, MATCH_TCP_UDP_PORTS);
Jian Lid9b5f552016-03-11 18:15:31 -0800421 if (matchTcpUdpPortsEnabled == null) {
422 log.info("Matching TCP/UDP fields is not configured, " +
423 "using current value of {}", matchTcpUdpPorts);
424 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100425 matchTcpUdpPorts = matchTcpUdpPortsEnabled;
426 log.info("Configured. Matching TCP/UDP fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800427 matchTcpUdpPorts ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100428 }
Jian Lid9b5f552016-03-11 18:15:31 -0800429
430 Boolean matchIcmpFieldsEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700431 Tools.isPropertyEnabled(properties, MATCH_ICMP_FIELDS);
Jian Lid9b5f552016-03-11 18:15:31 -0800432 if (matchIcmpFieldsEnabled == null) {
433 log.info("Matching ICMP (v4 and v6) fields is not configured, " +
434 "using current value of {}", matchIcmpFields);
435 } else {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100436 matchIcmpFields = matchIcmpFieldsEnabled;
437 log.info("Configured. Matching ICMP (v4 and v6) fields is {}",
Jian Lid9b5f552016-03-11 18:15:31 -0800438 matchIcmpFields ? "enabled" : "disabled");
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100439 }
Rusty Eddy29acad62015-07-07 19:33:47 -0700440
Jian Lid9b5f552016-03-11 18:15:31 -0800441 Boolean ignoreIpv4McastPacketsEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700442 Tools.isPropertyEnabled(properties, IGNORE_IPV4_MCAST_PACKETS);
Jian Lid9b5f552016-03-11 18:15:31 -0800443 if (ignoreIpv4McastPacketsEnabled == null) {
444 log.info("Ignore IPv4 multi-cast packet is not configured, " +
Ray Milkeybd508ed2019-03-19 14:22:02 -0700445 "using current value of {}", ignoreIPv4Multicast);
Jian Lid9b5f552016-03-11 18:15:31 -0800446 } else {
Ray Milkeybd508ed2019-03-19 14:22:02 -0700447 ignoreIPv4Multicast = ignoreIpv4McastPacketsEnabled;
Rusty Eddy29acad62015-07-07 19:33:47 -0700448 log.info("Configured. Ignore IPv4 multicast packets is {}",
Ray Milkeybd508ed2019-03-19 14:22:02 -0700449 ignoreIPv4Multicast ? "enabled" : "disabled");
Rusty Eddy29acad62015-07-07 19:33:47 -0700450 }
kalagesa1a1867a2017-03-07 13:06:41 +0530451 Boolean recordMetricsEnabled =
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700452 Tools.isPropertyEnabled(properties, RECORD_METRICS);
kalagesa1a1867a2017-03-07 13:06:41 +0530453 if (recordMetricsEnabled == null) {
454 log.info("IConfigured. Ignore record metrics is {} ," +
455 "using current value of {}", recordMetrics);
456 } else {
457 recordMetrics = recordMetricsEnabled;
458 log.info("Configured. record metrics is {}",
459 recordMetrics ? "enabled" : "disabled");
460 }
461
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700462 flowTimeout = Tools.getIntegerProperty(properties, FLOW_TIMEOUT, FLOW_TIMEOUT_DEFAULT);
Yoonseon Han2f011ad2016-08-08 17:06:56 -0700463 log.info("Configured. Flow Timeout is configured to {} seconds", flowTimeout);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100464
Ray Milkey90fdd0e2018-10-30 15:27:12 -0700465 flowPriority = Tools.getIntegerProperty(properties, FLOW_PRIORITY, FLOW_PRIORITY_DEFAULT);
Jian Lid9b5f552016-03-11 18:15:31 -0800466 log.info("Configured. Flow Priority is configured to {}", flowPriority);
souvikdas95ce95eb72020-09-18 16:19:06 -0500467
468 Boolean inheritFlowTreatmentEnabled =
469 Tools.isPropertyEnabled(properties, INHERIT_FLOW_TREATMENT);
470 if (inheritFlowTreatmentEnabled == null) {
471 log.info("Inherit flow treatment is not configured, " +
472 "using current value of {}", inheritFlowTreatment);
473 } else {
474 inheritFlowTreatment = inheritFlowTreatmentEnabled;
475 log.info("Configured. Inherit flow treatment is {}",
476 inheritFlowTreatment ? "enabled" : "disabled");
477 }
tomc16656f2014-10-15 18:30:31 -0700478 }
tomc370ebd2014-09-16 01:25:21 -0700479
480 /**
481 * Packet processor responsible for forwarding packets along their paths.
482 */
483 private class ReactivePacketProcessor implements PacketProcessor {
484
485 @Override
486 public void process(PacketContext context) {
tomdc95b8a2014-09-17 08:07:26 -0700487 // Stop processing if the packet has been handled, since we
488 // can't do any more to it.
Sahil Lelea69534f2015-07-16 15:14:57 -0700489
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800490 if (context.isHandled()) {
alshabib7b2748f2014-09-16 20:21:11 -0700491 return;
492 }
tomdc95b8a2014-09-17 08:07:26 -0700493
tomc370ebd2014-09-16 01:25:21 -0700494 InboundPacket pkt = context.inPacket();
tom642b2262014-09-17 13:52:55 -0700495 Ethernet ethPkt = pkt.parsed();
alshabib6eb438a2014-10-01 16:39:37 -0700496
Jonathan Harte8600eb2015-01-12 10:30:45 -0800497 if (ethPkt == null) {
498 return;
499 }
500
kalagesa1a1867a2017-03-07 13:06:41 +0530501 MacAddress macAddress = ethPkt.getSourceMAC();
502 ReactiveForwardMetrics macMetrics = null;
503 macMetrics = createCounter(macAddress);
504 inPacket(macMetrics);
505
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900506 // Bail if this is deemed to be a control packet.
507 if (isControlPacket(ethPkt)) {
kalagesa1a1867a2017-03-07 13:06:41 +0530508 droppedPacket(macMetrics);
Kunihiro Ishigurod37c9ca2014-12-31 16:05:40 +0900509 return;
510 }
511
512 // Skip IPv6 multicast packet when IPv6 forward is disabled.
513 if (!ipv6Forwarding && isIpv6Multicast(ethPkt)) {
kalagesa1a1867a2017-03-07 13:06:41 +0530514 droppedPacket(macMetrics);
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800515 return;
516 }
517
Simon Buttgereit3043bb32019-11-13 09:09:13 +0100518 HostId id = HostId.hostId(ethPkt.getDestinationMAC(), VlanId.vlanId(ethPkt.getVlanID()));
tomc370ebd2014-09-16 01:25:21 -0700519
Charles Chan928ff8b2017-05-04 12:22:54 -0700520 // Do not process LLDP MAC address in any way.
521 if (id.mac().isLldp()) {
kalagesa1a1867a2017-03-07 13:06:41 +0530522 droppedPacket(macMetrics);
Thomas Vachuskae1bcb0b2014-10-27 17:45:10 -0700523 return;
524 }
525
Rusty Eddy29acad62015-07-07 19:33:47 -0700526 // Do not process IPv4 multicast packets, let mfwd handle them
Ray Milkeybd508ed2019-03-19 14:22:02 -0700527 if (ignoreIPv4Multicast && ethPkt.getEtherType() == Ethernet.TYPE_IPV4) {
Rusty Eddy29acad62015-07-07 19:33:47 -0700528 if (id.mac().isMulticast()) {
529 return;
530 }
531 }
532
tomc370ebd2014-09-16 01:25:21 -0700533 // Do we know who this is for? If not, flood and bail.
534 Host dst = hostService.getHost(id);
535 if (dst == null) {
kalagesa1a1867a2017-03-07 13:06:41 +0530536 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700537 return;
538 }
539
540 // Are we on an edge switch that our destination is on? If so,
541 // simply forward out to the destination and bail.
542 if (pkt.receivedFrom().deviceId().equals(dst.location().deviceId())) {
alshabib6eb438a2014-10-01 16:39:37 -0700543 if (!context.inPacket().receivedFrom().port().equals(dst.location().port())) {
kalagesa1a1867a2017-03-07 13:06:41 +0530544 installRule(context, dst.location().port(), macMetrics);
alshabib6eb438a2014-10-01 16:39:37 -0700545 }
tomc370ebd2014-09-16 01:25:21 -0700546 return;
547 }
548
549 // Otherwise, get a set of paths that lead from here to the
550 // destination edge switch.
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100551 Set<Path> paths =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700552 topologyService.getPaths(topologyService.currentTopology(),
553 pkt.receivedFrom().deviceId(),
554 dst.location().deviceId());
tomc370ebd2014-09-16 01:25:21 -0700555 if (paths.isEmpty()) {
556 // If there are no paths, flood and bail.
kalagesa1a1867a2017-03-07 13:06:41 +0530557 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700558 return;
559 }
560
561 // Otherwise, pick a path that does not lead back to where we
562 // came from; if no such path, flood and bail.
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700563 Path path = pickForwardPathIfPossible(paths, pkt.receivedFrom().port());
tomc370ebd2014-09-16 01:25:21 -0700564 if (path == null) {
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700565 log.warn("Don't know where to go from here {} for {} -> {}",
566 pkt.receivedFrom(), ethPkt.getSourceMAC(), ethPkt.getDestinationMAC());
kalagesa1a1867a2017-03-07 13:06:41 +0530567 flood(context, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700568 return;
569 }
570
571 // Otherwise forward and be done with it.
kalagesa1a1867a2017-03-07 13:06:41 +0530572 installRule(context, path.src().port(), macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700573 }
Thomas Vachuska01a6ec02014-11-05 09:54:09 -0800574
575 }
576
577 // Indicates whether this is a control packet, e.g. LLDP, BDDP
578 private boolean isControlPacket(Ethernet eth) {
579 short type = eth.getEtherType();
580 return type == Ethernet.TYPE_LLDP || type == Ethernet.TYPE_BSN;
tomc370ebd2014-09-16 01:25:21 -0700581 }
582
Thomas Vachuska5dd52f72014-11-28 19:27:45 -0800583 // Indicated whether this is an IPv6 multicast packet.
584 private boolean isIpv6Multicast(Ethernet eth) {
585 return eth.getEtherType() == Ethernet.TYPE_IPV6 && eth.isMulticast();
586 }
587
tomc370ebd2014-09-16 01:25:21 -0700588 // Selects a path from the given set that does not lead back to the
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700589 // specified port if possible.
590 private Path pickForwardPathIfPossible(Set<Path> paths, PortNumber notToPort) {
tomc370ebd2014-09-16 01:25:21 -0700591 for (Path path : paths) {
592 if (!path.src().port().equals(notToPort)) {
593 return path;
594 }
595 }
Cheng-An Chuangd0a70f52017-08-10 21:20:32 +0800596 return null;
tomc370ebd2014-09-16 01:25:21 -0700597 }
598
tom642b2262014-09-17 13:52:55 -0700599 // Floods the specified packet if permissible.
kalagesa1a1867a2017-03-07 13:06:41 +0530600 private void flood(PacketContext context, ReactiveForwardMetrics macMetrics) {
tomdc95b8a2014-09-17 08:07:26 -0700601 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
tomc16656f2014-10-15 18:30:31 -0700602 context.inPacket().receivedFrom())) {
kalagesa1a1867a2017-03-07 13:06:41 +0530603 packetOut(context, PortNumber.FLOOD, macMetrics);
tomc370ebd2014-09-16 01:25:21 -0700604 } else {
605 context.block();
606 }
607 }
608
tom642b2262014-09-17 13:52:55 -0700609 // Sends a packet out the specified port.
kalagesa1a1867a2017-03-07 13:06:41 +0530610 private void packetOut(PacketContext context, PortNumber portNumber, ReactiveForwardMetrics macMetrics) {
611 replyPacket(macMetrics);
alshabib010c31d2014-09-26 10:01:12 -0700612 context.treatmentBuilder().setOutput(portNumber);
alshabib7b2748f2014-09-16 20:21:11 -0700613 context.send();
614 }
615
616 // Install a rule forwarding the packet to the specified port.
kalagesa1a1867a2017-03-07 13:06:41 +0530617 private void installRule(PacketContext context, PortNumber portNumber, ReactiveForwardMetrics macMetrics) {
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100618 //
619 // We don't support (yet) buffer IDs in the Flow Service so
620 // packet out first.
621 //
622 Ethernet inPkt = context.inPacket().parsed();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700623 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100624
625 // If PacketOutOnly or ARP packet than forward directly to output port
626 if (packetOutOnly || inPkt.getEtherType() == Ethernet.TYPE_ARP) {
kalagesa1a1867a2017-03-07 13:06:41 +0530627 packetOut(context, portNumber, macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100628 return;
629 }
630
631 //
632 // If matchDstMacOnly
633 // Create flows matching dstMac only
634 // Else
635 // Create flows with default matching and include configured fields
636 //
637 if (matchDstMacOnly) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700638 selectorBuilder.matchEthDst(inPkt.getDestinationMAC());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100639 } else {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700640 selectorBuilder.matchInPort(context.inPacket().receivedFrom().port())
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800641 .matchEthSrc(inPkt.getSourceMAC())
Jonathan Hart430223a2015-04-22 17:39:02 -0700642 .matchEthDst(inPkt.getDestinationMAC());
alshabib7b2748f2014-09-16 20:21:11 -0700643
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100644 // If configured Match Vlan ID
645 if (matchVlanId && inPkt.getVlanID() != Ethernet.VLAN_UNTAGGED) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700646 selectorBuilder.matchVlanId(VlanId.vlanId(inPkt.getVlanID()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100647 }
alshabib7b2748f2014-09-16 20:21:11 -0700648
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100649 //
650 // If configured and EtherType is IPv4 - Match IPv4 and
651 // TCP/UDP/ICMP fields
652 //
653 if (matchIpv4Address && inPkt.getEtherType() == Ethernet.TYPE_IPV4) {
654 IPv4 ipv4Packet = (IPv4) inPkt.getPayload();
655 byte ipv4Protocol = ipv4Packet.getProtocol();
656 Ip4Prefix matchIp4SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700657 Ip4Prefix.valueOf(ipv4Packet.getSourceAddress(),
658 Ip4Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100659 Ip4Prefix matchIp4DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700660 Ip4Prefix.valueOf(ipv4Packet.getDestinationAddress(),
661 Ip4Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800662 selectorBuilder.matchEthType(Ethernet.TYPE_IPV4)
Jonathan Hart430223a2015-04-22 17:39:02 -0700663 .matchIPSrc(matchIp4SrcPrefix)
664 .matchIPDst(matchIp4DstPrefix);
alshabib6eb438a2014-10-01 16:39:37 -0700665
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100666 if (matchIpv4Dscp) {
Pavlin Radoslavovbf23c552015-02-20 14:20:30 -0800667 byte dscp = ipv4Packet.getDscp();
668 byte ecn = ipv4Packet.getEcn();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700669 selectorBuilder.matchIPDscp(dscp).matchIPEcn(ecn);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100670 }
671
672 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_TCP) {
673 TCP tcpPacket = (TCP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700674 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700675 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
676 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100677 }
678 if (matchTcpUdpPorts && ipv4Protocol == IPv4.PROTOCOL_UDP) {
679 UDP udpPacket = (UDP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700680 selectorBuilder.matchIPProtocol(ipv4Protocol)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700681 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
682 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100683 }
684 if (matchIcmpFields && ipv4Protocol == IPv4.PROTOCOL_ICMP) {
685 ICMP icmpPacket = (ICMP) ipv4Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700686 selectorBuilder.matchIPProtocol(ipv4Protocol)
Jonathan Hart430223a2015-04-22 17:39:02 -0700687 .matchIcmpType(icmpPacket.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100688 .matchIcmpCode(icmpPacket.getIcmpCode());
689 }
690 }
691
692 //
693 // If configured and EtherType is IPv6 - Match IPv6 and
694 // TCP/UDP/ICMP fields
695 //
696 if (matchIpv6Address && inPkt.getEtherType() == Ethernet.TYPE_IPV6) {
697 IPv6 ipv6Packet = (IPv6) inPkt.getPayload();
698 byte ipv6NextHeader = ipv6Packet.getNextHeader();
699 Ip6Prefix matchIp6SrcPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700700 Ip6Prefix.valueOf(ipv6Packet.getSourceAddress(),
701 Ip6Prefix.MAX_MASK_LENGTH);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100702 Ip6Prefix matchIp6DstPrefix =
Thomas Vachuska27bee092015-06-23 19:03:10 -0700703 Ip6Prefix.valueOf(ipv6Packet.getDestinationAddress(),
704 Ip6Prefix.MAX_MASK_LENGTH);
Charles M.C. Chan7b8a9212015-04-26 01:25:53 +0800705 selectorBuilder.matchEthType(Ethernet.TYPE_IPV6)
706 .matchIPv6Src(matchIp6SrcPrefix)
Jonathan Hart430223a2015-04-22 17:39:02 -0700707 .matchIPv6Dst(matchIp6DstPrefix);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100708
709 if (matchIpv6FlowLabel) {
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700710 selectorBuilder.matchIPv6FlowLabel(ipv6Packet.getFlowLabel());
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100711 }
712
713 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_TCP) {
714 TCP tcpPacket = (TCP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700715 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700716 .matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
717 .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100718 }
719 if (matchTcpUdpPorts && ipv6NextHeader == IPv6.PROTOCOL_UDP) {
720 UDP udpPacket = (UDP) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700721 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Hyunsun Mooncf732fb2015-08-22 21:04:23 -0700722 .matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
723 .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100724 }
725 if (matchIcmpFields && ipv6NextHeader == IPv6.PROTOCOL_ICMP6) {
726 ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700727 selectorBuilder.matchIPProtocol(ipv6NextHeader)
Jonathan Hart430223a2015-04-22 17:39:02 -0700728 .matchIcmpv6Type(icmp6Packet.getIcmpType())
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100729 .matchIcmpv6Code(icmp6Packet.getIcmpCode());
730 }
731 }
732 }
souvikdas95ce95eb72020-09-18 16:19:06 -0500733 TrafficTreatment treatment;
734 if (inheritFlowTreatment) {
735 treatment = context.treatmentBuilder()
736 .setOutput(portNumber)
737 .build();
738 } else {
739 treatment = DefaultTrafficTreatment.builder()
740 .setOutput(portNumber)
741 .build();
742 }
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100743
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700744 ForwardingObjective forwardingObjective = DefaultForwardingObjective.builder()
745 .withSelector(selectorBuilder.build())
746 .withTreatment(treatment)
747 .withPriority(flowPriority)
748 .withFlag(ForwardingObjective.Flag.VERSATILE)
749 .fromApp(appId)
750 .makeTemporary(flowTimeout)
751 .add();
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100752
Jonathan Hart3b881aa2015-04-22 18:03:50 -0700753 flowObjectiveService.forward(context.inPacket().receivedFrom().deviceId(),
754 forwardingObjective);
kalagesa1a1867a2017-03-07 13:06:41 +0530755 forwardPacket(macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100756 //
757 // If packetOutOfppTable
758 // Send packet back to the OpenFlow pipeline to match installed flow
759 // Else
760 // Send packet direction on the appropriate port
761 //
762 if (packetOutOfppTable) {
kalagesa1a1867a2017-03-07 13:06:41 +0530763 packetOut(context, PortNumber.TABLE, macMetrics);
Dusan Pajin0d1d48f2015-02-20 16:05:11 +0100764 } else {
kalagesa1a1867a2017-03-07 13:06:41 +0530765 packetOut(context, portNumber, macMetrics);
Thomas Vachuskabd7f4b32014-12-04 20:54:55 -0800766 }
tomc370ebd2014-09-16 01:25:21 -0700767 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700768
kalagesa1a1867a2017-03-07 13:06:41 +0530769
Sahil Lelea69534f2015-07-16 15:14:57 -0700770 private class InternalTopologyListener implements TopologyListener {
771 @Override
772 public void event(TopologyEvent event) {
773 List<Event> reasons = event.reasons();
774 if (reasons != null) {
775 reasons.forEach(re -> {
776 if (re instanceof LinkEvent) {
777 LinkEvent le = (LinkEvent) re;
Thomas Vachuskae9d21862017-10-03 14:05:48 -0700778 if (le.type() == LinkEvent.Type.LINK_REMOVED && blackHoleExecutor != null) {
779 blackHoleExecutor.submit(() -> fixBlackhole(le.subject().src()));
Sahil Lelea69534f2015-07-16 15:14:57 -0700780 }
781 }
782 });
783 }
784 }
785 }
786
787 private void fixBlackhole(ConnectPoint egress) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700788 Set<FlowEntry> rules = getFlowRulesFrom(egress);
Sahil Lelea69534f2015-07-16 15:14:57 -0700789 Set<SrcDstPair> pairs = findSrcDstPairs(rules);
790
Sahil Lele7b961662015-07-20 15:06:04 -0700791 Map<DeviceId, Set<Path>> srcPaths = new HashMap<>();
792
Thomas Vachuska8d033672015-07-21 16:15:04 -0700793 for (SrcDstPair sd : pairs) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700794 // get the edge deviceID for the src host
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700795 Host srcHost = hostService.getHost(HostId.hostId(sd.src));
796 Host dstHost = hostService.getHost(HostId.hostId(sd.dst));
797 if (srcHost != null && dstHost != null) {
798 DeviceId srcId = srcHost.location().deviceId();
799 DeviceId dstId = dstHost.location().deviceId();
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800800 log.trace("SRC ID is {}, DST ID is {}", srcId, dstId);
Sahil Lelea69534f2015-07-16 15:14:57 -0700801
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700802 cleanFlowRules(sd, egress.deviceId());
Sahil Lelea69534f2015-07-16 15:14:57 -0700803
Sahil Lele0f8d00e2015-07-24 11:05:43 -0700804 Set<Path> shortestPaths = srcPaths.get(srcId);
805 if (shortestPaths == null) {
806 shortestPaths = topologyService.getPaths(topologyService.currentTopology(),
807 egress.deviceId(), srcId);
808 srcPaths.put(srcId, shortestPaths);
809 }
810 backTrackBadNodes(shortestPaths, dstId, sd);
Sahil Lele7b961662015-07-20 15:06:04 -0700811 }
Sahil Lelea69534f2015-07-16 15:14:57 -0700812 }
813 }
814
815 // Backtracks from link down event to remove flows that lead to blackhole
816 private void backTrackBadNodes(Set<Path> shortestPaths, DeviceId dstId, SrcDstPair sd) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700817 for (Path p : shortestPaths) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700818 List<Link> pathLinks = p.links();
819 for (int i = 0; i < pathLinks.size(); i = i + 1) {
820 Link curLink = pathLinks.get(i);
821 DeviceId curDevice = curLink.src().deviceId();
Sahil Lelea69534f2015-07-16 15:14:57 -0700822
823 // skipping the first link because this link's src has already been pruned beforehand
824 if (i != 0) {
825 cleanFlowRules(sd, curDevice);
826 }
827
Thomas Vachuska8d033672015-07-21 16:15:04 -0700828 Set<Path> pathsFromCurDevice =
829 topologyService.getPaths(topologyService.currentTopology(),
830 curDevice, dstId);
Thomas Vachuska320c58f2015-08-05 10:42:32 -0700831 if (pickForwardPathIfPossible(pathsFromCurDevice, curLink.src().port()) != null) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700832 break;
833 } else {
834 if (i + 1 == pathLinks.size()) {
835 cleanFlowRules(sd, curLink.dst().deviceId());
836 }
837 }
838 }
839 }
840 }
841
842 // Removes flow rules off specified device with specific SrcDstPair
843 private void cleanFlowRules(SrcDstPair pair, DeviceId id) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800844 log.trace("Searching for flow rules to remove from: {}", id);
845 log.trace("Removing flows w/ SRC={}, DST={}", pair.src, pair.dst);
Sahil Lelea69534f2015-07-16 15:14:57 -0700846 for (FlowEntry r : flowRuleService.getFlowEntries(id)) {
847 boolean matchesSrc = false, matchesDst = false;
848 for (Instruction i : r.treatment().allInstructions()) {
849 if (i.type() == Instruction.Type.OUTPUT) {
Thomas Vachuska8d033672015-07-21 16:15:04 -0700850 // if the flow has matching src and dst
Sahil Lelea69534f2015-07-16 15:14:57 -0700851 for (Criterion cr : r.selector().criteria()) {
852 if (cr.type() == Criterion.Type.ETH_DST) {
853 if (((EthCriterion) cr).mac().equals(pair.dst)) {
854 matchesDst = true;
855 }
856 } else if (cr.type() == Criterion.Type.ETH_SRC) {
857 if (((EthCriterion) cr).mac().equals(pair.src)) {
858 matchesSrc = true;
859 }
860 }
861 }
862 }
863 }
864 if (matchesDst && matchesSrc) {
Yuta HIGUCHI59bde762017-02-17 09:45:57 -0800865 log.trace("Removed flow rule from device: {}", id);
Sahil Lelea69534f2015-07-16 15:14:57 -0700866 flowRuleService.removeFlowRules((FlowRule) r);
867 }
868 }
869
870 }
871
872 // Returns a set of src/dst MAC pairs extracted from the specified set of flow entries
873 private Set<SrcDstPair> findSrcDstPairs(Set<FlowEntry> rules) {
874 ImmutableSet.Builder<SrcDstPair> builder = ImmutableSet.builder();
Thomas Vachuska8d033672015-07-21 16:15:04 -0700875 for (FlowEntry r : rules) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700876 MacAddress src = null, dst = null;
Thomas Vachuska8d033672015-07-21 16:15:04 -0700877 for (Criterion cr : r.selector().criteria()) {
Sahil Lelea69534f2015-07-16 15:14:57 -0700878 if (cr.type() == Criterion.Type.ETH_DST) {
879 dst = ((EthCriterion) cr).mac();
880 } else if (cr.type() == Criterion.Type.ETH_SRC) {
881 src = ((EthCriterion) cr).mac();
882 }
883 }
884 builder.add(new SrcDstPair(src, dst));
885 }
886 return builder.build();
887 }
888
kalagesa1a1867a2017-03-07 13:06:41 +0530889 private ReactiveForwardMetrics createCounter(MacAddress macAddress) {
890 ReactiveForwardMetrics macMetrics = null;
891 if (recordMetrics) {
892 macMetrics = metrics.compute(macAddress, (key, existingValue) -> {
893 if (existingValue == null) {
894 return new ReactiveForwardMetrics(0L, 0L, 0L, 0L, macAddress);
895 } else {
896 return existingValue;
897 }
898 });
899 }
900 return macMetrics;
901 }
902
903 private void forwardPacket(ReactiveForwardMetrics macmetrics) {
904 if (recordMetrics) {
905 macmetrics.incrementForwardedPacket();
906 metrics.put(macmetrics.getMacAddress(), macmetrics);
907 }
908 }
909
910 private void inPacket(ReactiveForwardMetrics macmetrics) {
911 if (recordMetrics) {
912 macmetrics.incrementInPacket();
913 metrics.put(macmetrics.getMacAddress(), macmetrics);
914 }
915 }
916
917 private void replyPacket(ReactiveForwardMetrics macmetrics) {
918 if (recordMetrics) {
919 macmetrics.incremnetReplyPacket();
920 metrics.put(macmetrics.getMacAddress(), macmetrics);
921 }
922 }
923
924 private void droppedPacket(ReactiveForwardMetrics macmetrics) {
925 if (recordMetrics) {
926 macmetrics.incrementDroppedPacket();
927 metrics.put(macmetrics.getMacAddress(), macmetrics);
928 }
929 }
930
931 public EventuallyConsistentMap<MacAddress, ReactiveForwardMetrics> getMacAddress() {
932 return metrics;
933 }
934
935 public void printMetric(MacAddress mac) {
936 System.out.println("-----------------------------------------------------------------------------------------");
937 System.out.println(" MACADDRESS \t\t\t\t\t\t Metrics");
938 if (mac != null) {
939 System.out.println(" " + mac + " \t\t\t " + metrics.get(mac));
940 } else {
941 for (MacAddress key : metrics.keySet()) {
942 System.out.println(" " + key + " \t\t\t " + metrics.get(key));
943 }
944 }
945 }
946
Sahil Lelea69534f2015-07-16 15:14:57 -0700947 private Set<FlowEntry> getFlowRulesFrom(ConnectPoint egress) {
948 ImmutableSet.Builder<FlowEntry> builder = ImmutableSet.builder();
949 flowRuleService.getFlowEntries(egress.deviceId()).forEach(r -> {
950 if (r.appId() == appId.id()) {
951 r.treatment().allInstructions().forEach(i -> {
952 if (i.type() == Instruction.Type.OUTPUT) {
953 if (((Instructions.OutputInstruction) i).port().equals(egress.port())) {
954 builder.add(r);
955 }
956 }
957 });
958 }
959 });
960
961 return builder.build();
962 }
963
964 // Wrapper class for a source and destination pair of MAC addresses
965 private final class SrcDstPair {
966 final MacAddress src;
967 final MacAddress dst;
968
969 private SrcDstPair(MacAddress src, MacAddress dst) {
970 this.src = src;
971 this.dst = dst;
972 }
973
974 @Override
975 public boolean equals(Object o) {
976 if (this == o) {
977 return true;
978 }
979 if (o == null || getClass() != o.getClass()) {
980 return false;
981 }
982 SrcDstPair that = (SrcDstPair) o;
983 return Objects.equals(src, that.src) &&
984 Objects.equals(dst, that.dst);
985 }
986
987 @Override
988 public int hashCode() {
989 return Objects.hash(src, dst);
990 }
991 }
alshabib030111e2014-09-15 15:56:42 -0700992}