blob: d7b2d45b5c52aacc68c087f01f523894133ae9c3 [file] [log] [blame]
Simon Hunt026a2872017-11-13 17:09:43 -08001/*
2 * Copyright 2017-present Open Networking Foundation
3 *
4 * 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
7 *
8 * 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.
15 */
16
17package org.onosproject.t3.impl;
18
Andrea Campanella17d45192018-01-18 17:11:42 +010019import com.google.common.base.Preconditions;
Andrea Campanellae4084402017-12-15 15:27:31 +010020import com.google.common.collect.ImmutableList;
Andrea Campanella696ef032018-02-27 18:03:17 +010021import com.google.common.collect.ImmutableSet;
Andrea Campanellae4084402017-12-15 15:27:31 +010022import com.google.common.collect.Lists;
Andrea Campanella5befe1c2018-02-27 14:50:45 +010023import com.google.common.collect.Sets;
pierventrefe57fda2020-08-04 22:52:02 +020024import com.google.common.collect.Maps;
Seyeon Jeong5018bdd2020-02-28 01:17:34 -080025import org.apache.commons.lang.StringUtils;
Andrea Campanella6a614fa2018-02-21 14:28:20 +010026import org.apache.commons.lang3.tuple.Pair;
Andrea Campanella55c3f422018-02-08 17:10:11 +010027import org.onlab.packet.IpAddress;
Andrea Campanellae4084402017-12-15 15:27:31 +010028import org.onlab.packet.VlanId;
pierventrefe57fda2020-08-04 22:52:02 +020029import org.onlab.util.Generator;
Andrea Campanella54923d62018-01-23 12:46:04 +010030import org.onosproject.cluster.NodeId;
pierventrefe57fda2020-08-04 22:52:02 +020031
Simon Hunt026a2872017-11-13 17:09:43 -080032import org.onosproject.net.ConnectPoint;
pierventrefe57fda2020-08-04 22:52:02 +020033import org.onosproject.net.DataPlaneEntity;
34import org.onosproject.net.Device;
Andrea Campanellae4084402017-12-15 15:27:31 +010035import org.onosproject.net.DeviceId;
36import org.onosproject.net.Host;
Andrea Campanella55c3f422018-02-08 17:10:11 +010037import org.onosproject.net.HostId;
Andrea Campanellae4084402017-12-15 15:27:31 +010038import org.onosproject.net.Link;
pierventrefe57fda2020-08-04 22:52:02 +020039import org.onosproject.net.PipelineTraceableHitChain;
40import org.onosproject.net.PipelineTraceableInput;
41import org.onosproject.net.PipelineTraceableOutput;
42import org.onosproject.net.PipelineTraceableOutput.PipelineTraceableResult;
pierventre4fd3b462020-10-15 17:31:44 +020043import org.onosproject.net.PipelineTraceablePacket;
Andrea Campanella1445f7a2018-02-07 12:00:12 +010044import org.onosproject.net.Port;
Andrea Campanellae4084402017-12-15 15:27:31 +010045import org.onosproject.net.PortNumber;
pierventrefe57fda2020-08-04 22:52:02 +020046import org.onosproject.net.behaviour.PipelineTraceable;
Andrea Campanella55c3f422018-02-08 17:10:11 +010047import org.onosproject.net.config.ConfigException;
Andrea Campanella55c3f422018-02-08 17:10:11 +010048import org.onosproject.net.config.basics.InterfaceConfig;
Andrea Campanellae4084402017-12-15 15:27:31 +010049import org.onosproject.net.flow.DefaultTrafficSelector;
50import org.onosproject.net.flow.FlowEntry;
Simon Hunt026a2872017-11-13 17:09:43 -080051import org.onosproject.net.flow.TrafficSelector;
Andrea Campanellae4084402017-12-15 15:27:31 +010052import org.onosproject.net.flow.criteria.Criteria;
53import org.onosproject.net.flow.criteria.Criterion;
54import org.onosproject.net.flow.criteria.EthCriterion;
55import org.onosproject.net.flow.criteria.EthTypeCriterion;
56import org.onosproject.net.flow.criteria.IPCriterion;
Andrea Campanellae6798012018-02-06 15:46:52 +010057import org.onosproject.net.flow.criteria.VlanIdCriterion;
Andrea Campanellae4084402017-12-15 15:27:31 +010058import org.onosproject.net.group.Group;
Andrea Campanella55c3f422018-02-08 17:10:11 +010059import org.onosproject.net.host.InterfaceIpAddress;
60import org.onosproject.net.intf.Interface;
Andrea Campanella08d07e12018-03-07 14:27:54 -080061import org.onosproject.routeservice.ResolvedRoute;
Andrea Campanella55c3f422018-02-08 17:10:11 +010062import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
Seyeon Jeong83e79862020-02-28 01:17:34 -080063import org.onosproject.t3.api.DeviceNib;
64import org.onosproject.t3.api.DriverNib;
65import org.onosproject.t3.api.EdgePortNib;
66import org.onosproject.t3.api.FlowNib;
67import org.onosproject.t3.api.GroupNib;
Seyeon Jeong83e79862020-02-28 01:17:34 -080068import org.onosproject.t3.api.HostNib;
69import org.onosproject.t3.api.LinkNib;
70import org.onosproject.t3.api.MastershipNib;
71import org.onosproject.t3.api.MulticastRouteNib;
72import org.onosproject.t3.api.NetworkConfigNib;
Seyeon Jeong5018bdd2020-02-28 01:17:34 -080073import org.onosproject.t3.api.NibProfile;
Seyeon Jeong83e79862020-02-28 01:17:34 -080074import org.onosproject.t3.api.RouteNib;
Simon Hunt026a2872017-11-13 17:09:43 -080075import org.onosproject.t3.api.StaticPacketTrace;
76import org.onosproject.t3.api.TroubleshootService;
Ray Milkey9511b512018-08-17 14:54:17 -070077import org.osgi.service.component.annotations.Component;
Simon Hunt026a2872017-11-13 17:09:43 -080078import org.slf4j.Logger;
79
pierventrefe57fda2020-08-04 22:52:02 +020080import java.util.Map;
Andrea Campanellae4084402017-12-15 15:27:31 +010081import java.util.List;
82import java.util.Set;
pierventrefe57fda2020-08-04 22:52:02 +020083import java.util.HashSet;
84import java.util.Optional;
85import java.util.ArrayList;
86import java.util.Collections;
87
Andrea Campanellae4084402017-12-15 15:27:31 +010088import java.util.stream.Collectors;
Seyeon Jeong83e79862020-02-28 01:17:34 -080089import java.util.stream.Stream;
psnehab823c512018-08-02 07:41:43 -040090import java.util.stream.StreamSupport;
Andrea Campanellae4084402017-12-15 15:27:31 +010091
92import static org.onlab.packet.EthType.EtherType;
Andrea Campanellae6798012018-02-06 15:46:52 +010093import static org.onosproject.net.flow.TrafficSelector.Builder;
Simon Hunt026a2872017-11-13 17:09:43 -080094import static org.slf4j.LoggerFactory.getLogger;
95
96/**
Andrea Campanellae4084402017-12-15 15:27:31 +010097 * Manager to troubleshoot packets inside the network.
98 * Given a representation of a packet follows it's path in the network according to the existing flows and groups in
99 * the devices.
Simon Hunt026a2872017-11-13 17:09:43 -0800100 */
Ray Milkey9511b512018-08-17 14:54:17 -0700101@Component(immediate = true, service = TroubleshootService.class)
Simon Hunt026a2872017-11-13 17:09:43 -0800102public class TroubleshootManager implements TroubleshootService {
103
104 private static final Logger log = getLogger(TroubleshootManager.class);
105
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100106 static final String PACKET_TO_CONTROLLER = "Packet goes to the controller";
107
Seyeon Jeong83e79862020-02-28 01:17:34 -0800108 // uses a snapshot (cache) of NIBs instead of interacting with ONOS core in runtime
Seyeon Jeong5018bdd2020-02-28 01:17:34 -0800109 protected FlowNib flowNib = FlowNib.getInstance();
110 protected GroupNib groupNib = GroupNib.getInstance();
111 protected LinkNib linkNib = LinkNib.getInstance();
112 protected HostNib hostNib = HostNib.getInstance();
113 protected DeviceNib deviceNib = DeviceNib.getInstance();
114 protected DriverNib driverNib = DriverNib.getInstance();
115 protected MastershipNib mastershipNib = MastershipNib.getInstance();
116 protected EdgePortNib edgePortNib = EdgePortNib.getInstance();
117 protected RouteNib routeNib = RouteNib.getInstance();
118 protected NetworkConfigNib networkConfigNib = NetworkConfigNib.getInstance();
119 protected MulticastRouteNib mcastRouteNib = MulticastRouteNib.getInstance();
Simon Hunt026a2872017-11-13 17:09:43 -0800120
pierventrefe57fda2020-08-04 22:52:02 +0200121 // FIXME Revisit offline mode after a first implementation
122 private final Map<DeviceId, PipelineTraceable> pipelineTraceables = Maps.newConcurrentMap();
123
Seyeon Jeong83e79862020-02-28 01:17:34 -0800124 @Override
Seyeon Jeong5018bdd2020-02-28 01:17:34 -0800125 public boolean checkNibValidity() {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800126 return Stream.of(flowNib, groupNib, linkNib, hostNib, deviceNib, driverNib,
127 mastershipNib, edgePortNib, routeNib, networkConfigNib, mcastRouteNib)
Seyeon Jeong5018bdd2020-02-28 01:17:34 -0800128 .allMatch(nib -> nib != null && nib.isValid());
Seyeon Jeong83e79862020-02-28 01:17:34 -0800129 }
Simon Hunt026a2872017-11-13 17:09:43 -0800130
Seyeon Jeong83e79862020-02-28 01:17:34 -0800131 @Override
Seyeon Jeong5018bdd2020-02-28 01:17:34 -0800132 public String printNibSummary() {
133 StringBuilder summary = new StringBuilder().append("*** Current NIB in valid: ***\n");
134 Stream.of(flowNib, groupNib, linkNib, hostNib, deviceNib, driverNib,
135 mastershipNib, edgePortNib, routeNib, networkConfigNib, mcastRouteNib)
136 .forEach(nib -> {
137 NibProfile profile = nib.getProfile();
138 summary.append(String.format(
139 nib.getClass().getName() + " created %s from %s\n",
140 profile.date(), profile.sourceType()));
141 });
142
143 return summary.append(StringUtils.rightPad("", 125, '-')).toString();
Seyeon Jeong83e79862020-02-28 01:17:34 -0800144 }
Andrea Campanellabd15bf52018-04-06 16:30:18 +0200145
Andrea Campanella55c3f422018-02-08 17:10:11 +0100146 @Override
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100147 public Generator<Set<StaticPacketTrace>> pingAllGenerator(EtherType type) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800148 return new PingAllGenerator(type, hostNib, this);
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100149 }
150
151 @Override
Andrea Campanellabd15bf52018-04-06 16:30:18 +0200152 public Generator<Set<StaticPacketTrace>> traceMcast(VlanId vlanId) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800153 return new McastGenerator(mcastRouteNib, this, vlanId);
Andrea Campanellabd15bf52018-04-06 16:30:18 +0200154 }
155
156 @Override
Andrea Campanella696ef032018-02-27 18:03:17 +0100157 public Set<StaticPacketTrace> trace(HostId sourceHost, HostId destinationHost, EtherType etherType) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800158 Host source = hostNib.getHost(sourceHost);
159 Host destination = hostNib.getHost(destinationHost);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100160
Andrea Campanella6a614fa2018-02-21 14:28:20 +0100161 //Temporary trace to fail in case we don't have enough information or what is provided is incoherent
162 StaticPacketTrace failTrace = new StaticPacketTrace(null, null, Pair.of(source, destination));
Andrea Campanella55c3f422018-02-08 17:10:11 +0100163
164 if (source == null) {
165 failTrace.addResultMessage("Source Host " + sourceHost + " does not exist");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700166 failTrace.setSuccess(false);
167
Andrea Campanella696ef032018-02-27 18:03:17 +0100168 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100169 }
170
171 if (destination == null) {
172 failTrace.addResultMessage("Destination Host " + destinationHost + " does not exist");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700173 failTrace.setSuccess(false);
Andrea Campanella696ef032018-02-27 18:03:17 +0100174 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100175 }
176
177 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Andrea Campanella55c3f422018-02-08 17:10:11 +0100178 .matchEthType(etherType.ethType().toShort())
179 .matchEthDst(source.mac())
180 .matchVlanId(source.vlan());
181
182
Andrea Campanella55c3f422018-02-08 17:10:11 +0100183 try {
Andrea Campanellae24d4922018-03-26 10:39:22 -0700184 ImmutableSet.Builder<StaticPacketTrace> traces = ImmutableSet.builder();
Andrea Campanella55c3f422018-02-08 17:10:11 +0100185 //if the location deviceId is the same, the two hosts are under same subnet and vlan on the interface
186 // we are under same leaf so it's L2 Unicast.
187 if (areBridged(source, destination)) {
188 selectorBuilder.matchEthDst(destination.mac());
Andrea Campanella696ef032018-02-27 18:03:17 +0100189 source.locations().forEach(hostLocation -> {
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800190 selectorBuilder.matchInPort(hostLocation.port());
Andrea Campanella696ef032018-02-27 18:03:17 +0100191 StaticPacketTrace trace = trace(selectorBuilder.build(), hostLocation);
192 trace.addEndpointHosts(Pair.of(source, destination));
193 traces.add(trace);
194 });
Andrea Campanellae24d4922018-03-26 10:39:22 -0700195 //The destination host is not dual homed, if it is the other path might be done through routing.
196 if (destination.locations().size() == 1) {
197 return traces.build();
198 }
Andrea Campanella55c3f422018-02-08 17:10:11 +0100199 }
Andrea Campanella55c3f422018-02-08 17:10:11 +0100200 //handle the IPs for src and dst in case of L3
201 if (etherType.equals(EtherType.IPV4) || etherType.equals(EtherType.IPV6)) {
202
203 //Match on the source IP
204 if (!matchIP(source, failTrace, selectorBuilder, etherType, true)) {
Andrea Campanella696ef032018-02-27 18:03:17 +0100205 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100206 }
207
208 //Match on destination IP
209 if (!matchIP(destination, failTrace, selectorBuilder, etherType, false)) {
Andrea Campanella696ef032018-02-27 18:03:17 +0100210 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100211 }
212
213 } else {
214 failTrace.addResultMessage("Host based trace supports only IPv4 or IPv6 as EtherType, " +
215 "please use packet based");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700216 failTrace.setSuccess(false);
Andrea Campanella696ef032018-02-27 18:03:17 +0100217 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100218 }
219
220 //l3 unicast, we get the dst mac of the leaf the source is connected to from netcfg
Seyeon Jeong83e79862020-02-28 01:17:34 -0800221 SegmentRoutingDeviceConfig segmentRoutingConfig = networkConfigNib.getConfig(source.location()
Andrea Campanella55c3f422018-02-08 17:10:11 +0100222 .deviceId(), SegmentRoutingDeviceConfig.class);
223 if (segmentRoutingConfig != null) {
224 selectorBuilder.matchEthDst(segmentRoutingConfig.routerMac());
225 } else {
226 failTrace.addResultMessage("Can't get " + source.location().deviceId() +
227 " router MAC from segment routing config can't perform L3 tracing.");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700228 failTrace.setSuccess(false);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100229 }
Andrea Campanella696ef032018-02-27 18:03:17 +0100230 source.locations().forEach(hostLocation -> {
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800231 selectorBuilder.matchInPort(hostLocation.port());
Andrea Campanella696ef032018-02-27 18:03:17 +0100232 StaticPacketTrace trace = trace(selectorBuilder.build(), hostLocation);
233 trace.addEndpointHosts(Pair.of(source, destination));
234 traces.add(trace);
235 });
236 return traces.build();
Andrea Campanella55c3f422018-02-08 17:10:11 +0100237
238 } catch (ConfigException e) {
239 failTrace.addResultMessage("Can't get config " + e.getMessage());
Andrea Campanella696ef032018-02-27 18:03:17 +0100240 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100241 }
242 }
243
pierventrefe57fda2020-08-04 22:52:02 +0200244 private PipelineTraceable getPipelineMatchable(DeviceId deviceId) {
245 return pipelineTraceables.compute(deviceId, (k, v) -> {
246 if (v == null) {
247 log.info("PipelineMatchable not found for {}", deviceId);
248 Device d = deviceNib.getDevice(deviceId);
249 if (d.is(PipelineTraceable.class)) {
250 v = d.as(PipelineTraceable.class);
251 v.init();
252 } else {
253 log.warn("PipelineMatchable behaviour not supported for device {}",
254 deviceId);
255 }
256 }
257 return v;
258 });
259 }
260
261 private List<DataPlaneEntity> getDataPlaneEntities(DeviceId deviceId) {
262 List<DataPlaneEntity> dataPlaneEntities = Lists.newArrayList();
263 flowNib.getFlowEntriesByState(deviceId, FlowEntry.FlowEntryState.ADDED).forEach(entity ->
264 dataPlaneEntities.add(new DataPlaneEntity(entity)));
265 groupNib.getGroupsByState(deviceId, Group.GroupState.ADDED).forEach(entity ->
266 dataPlaneEntities.add(new DataPlaneEntity(entity)));
267 return dataPlaneEntities;
268 }
269
Andrea Campanella55c3f422018-02-08 17:10:11 +0100270 /**
271 * Matches src and dst IPs based on host information.
272 *
273 * @param host the host
274 * @param failTrace the trace to use in case of failure
275 * @param selectorBuilder the packet we are building to trace
276 * @param etherType the traffic type
277 * @param src is this src host or dst host
278 * @return true if properly matched
279 */
280 private boolean matchIP(Host host, StaticPacketTrace failTrace, Builder selectorBuilder,
281 EtherType etherType, boolean src) {
Andrea Campanella6a614fa2018-02-21 14:28:20 +0100282 List<IpAddress> ips = getIpAddresses(host, etherType, true);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100283
284 if (ips.size() > 0) {
Andrea Campanellabf8d9302018-02-21 14:43:21 +0100285 if (etherType.equals(EtherType.IPV4)) {
286 if (src) {
287 selectorBuilder.matchIPSrc(ips.get(0).toIpPrefix());
288 } else {
289 selectorBuilder.matchIPDst(ips.get(0).toIpPrefix());
290 }
291 } else if (etherType.equals(EtherType.IPV6)) {
292 if (src) {
293 selectorBuilder.matchIPv6Src(ips.get(0).toIpPrefix());
294 } else {
295 selectorBuilder.matchIPv6Dst(ips.get(0).toIpPrefix());
296 }
Andrea Campanella55c3f422018-02-08 17:10:11 +0100297 }
298 } else {
299 failTrace.addResultMessage("Host " + host + " has no " + etherType + " address");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700300 failTrace.setSuccess(false);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100301 return false;
302 }
303 return true;
304 }
305
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100306 List<IpAddress> getIpAddresses(Host host, EtherType etherType, boolean checklocal) {
Andrea Campanella6a614fa2018-02-21 14:28:20 +0100307 return host.ipAddresses().stream().filter(ipAddress -> {
308 boolean correctIp = false;
309 if (etherType.equals(EtherType.IPV4)) {
310 correctIp = ipAddress.isIp4();
311 } else if (etherType.equals(EtherType.IPV6)) {
312 correctIp = ipAddress.isIp6();
313 }
314 if (checklocal) {
315 correctIp = correctIp && !ipAddress.isLinkLocal();
316 }
317 return correctIp;
318 }).collect(Collectors.toList());
319 }
320
Andrea Campanella55c3f422018-02-08 17:10:11 +0100321 /**
322 * Checks that two hosts are bridged (L2Unicast).
323 *
324 * @param source the source host
325 * @param destination the destination host
326 * @return true if bridged.
327 * @throws ConfigException if config can't be properly retrieved
328 */
329 private boolean areBridged(Host source, Host destination) throws ConfigException {
330
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800331 //If the locations is not the same we don't even check vlan or subnets
332 if (Collections.disjoint(source.locations(), destination.locations())) {
333 return false;
334 }
335
336 if (!source.vlan().equals(VlanId.NONE) && !destination.vlan().equals(VlanId.NONE)
337 && !source.vlan().equals(destination.vlan())) {
Andrea Campanella55c3f422018-02-08 17:10:11 +0100338 return false;
339 }
340
Seyeon Jeong83e79862020-02-28 01:17:34 -0800341 InterfaceConfig interfaceCfgH1 = networkConfigNib.getConfig(source.location(), InterfaceConfig.class);
342 InterfaceConfig interfaceCfgH2 = networkConfigNib.getConfig(destination.location(), InterfaceConfig.class);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100343 if (interfaceCfgH1 != null && interfaceCfgH2 != null) {
344
345 //following can be optimized but for clarity is left as is
346 Interface intfH1 = interfaceCfgH1.getInterfaces().stream().findFirst().get();
347 Interface intfH2 = interfaceCfgH2.getInterfaces().stream().findFirst().get();
348
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800349 if (source.vlan().equals(VlanId.NONE) && !destination.vlan().equals(VlanId.NONE)) {
350 return intfH1.vlanUntagged().equals(destination.vlan()) ||
351 intfH1.vlanNative().equals(destination.vlan());
Andrea Campanella55c3f422018-02-08 17:10:11 +0100352 }
353
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800354 if (!source.vlan().equals(VlanId.NONE) && destination.vlan().equals(VlanId.NONE)) {
355 return intfH2.vlanUntagged().equals(source.vlan()) ||
356 intfH2.vlanNative().equals(source.vlan());
357 }
358
359 if (!intfH1.vlanNative().equals(intfH2.vlanNative())) {
Andrea Campanella55c3f422018-02-08 17:10:11 +0100360 return false;
361 }
362
363 if (!intfH1.vlanUntagged().equals(intfH2.vlanUntagged())) {
364 return false;
365 }
366
367 List<InterfaceIpAddress> intersection = new ArrayList<>(intfH1.ipAddressesList());
368 intersection.retainAll(intfH2.ipAddressesList());
369 if (intersection.size() == 0) {
370 return false;
371 }
372 }
373 return true;
374 }
375
Simon Hunt026a2872017-11-13 17:09:43 -0800376 @Override
377 public StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in) {
Andrea Campanellae4084402017-12-15 15:27:31 +0100378 log.info("Tracing packet {} coming in through {}", packet, in);
Andrea Campanella17d45192018-01-18 17:11:42 +0100379 //device must exist in ONOS
Seyeon Jeong83e79862020-02-28 01:17:34 -0800380 Preconditions.checkNotNull(deviceNib.getDevice(in.deviceId()),
Andrea Campanella17d45192018-01-18 17:11:42 +0100381 "Device " + in.deviceId() + " must exist in ONOS");
382
Andrea Campanellae4084402017-12-15 15:27:31 +0100383 StaticPacketTrace trace = new StaticPacketTrace(packet, in);
Andrea Campanella696ef032018-02-27 18:03:17 +0100384 boolean isDualHomed = getHosts(trace).stream().anyMatch(host -> host.locations().size() > 1);
Andrea Campanellae4084402017-12-15 15:27:31 +0100385 //FIXME this can be done recursively
Andrea Campanellae4084402017-12-15 15:27:31 +0100386 //Building output connect Points
387 List<ConnectPoint> path = new ArrayList<>();
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700388 trace = traceInDevice(trace, packet, in, isDualHomed, path);
Andrea Campanella696ef032018-02-27 18:03:17 +0100389 trace = getTrace(path, in, trace, isDualHomed);
Andrea Campanellae4084402017-12-15 15:27:31 +0100390 return trace;
391 }
392
393 /**
394 * Computes a trace for a give packet that start in the network at the given connect point.
395 *
396 * @param completePath the path traversed by the packet
397 * @param in the input connect point
398 * @param trace the trace to build
Andrea Campanella696ef032018-02-27 18:03:17 +0100399 * @param isDualHomed true if the trace we are doing starts or ends in a dual homed host
Andrea Campanellae4084402017-12-15 15:27:31 +0100400 * @return the build trace for that packet.
401 */
Andrea Campanella696ef032018-02-27 18:03:17 +0100402 private StaticPacketTrace getTrace(List<ConnectPoint> completePath, ConnectPoint in, StaticPacketTrace trace,
403 boolean isDualHomed) {
Andrea Campanellae4084402017-12-15 15:27:31 +0100404
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100405 log.debug("------------------------------------------------------------");
406
Andrea Campanellae4084402017-12-15 15:27:31 +0100407 //if the trace already contains the input connect point there is a loop
408 if (pathContainsDevice(completePath, in.deviceId())) {
409 trace.addResultMessage("Loop encountered in device " + in.deviceId());
Andrea Campanella3ddbadb2018-03-09 14:52:10 -0800410 completePath.add(in);
411 trace.addCompletePath(completePath);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700412 trace.setSuccess(false);
Andrea Campanellae4084402017-12-15 15:27:31 +0100413 return trace;
414 }
415
416 //let's add the input connect point
417 completePath.add(in);
418
419 //If the trace has no outputs for the given input we stop here
pierventrefe57fda2020-08-04 22:52:02 +0200420 if (trace.getHitChains(in.deviceId()) == null) {
421 TroubleshootUtils.computePath(completePath, trace, null);
Andrea Campanellae4084402017-12-15 15:27:31 +0100422 trace.addResultMessage("No output out of device " + in.deviceId() + ". Packet is dropped");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700423 trace.setSuccess(false);
Andrea Campanellae4084402017-12-15 15:27:31 +0100424 return trace;
425 }
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100426
Andrea Campanella27140da2018-04-09 14:22:32 +0200427 //If the trace has outputs we analyze them all
pierventrefe57fda2020-08-04 22:52:02 +0200428 for (PipelineTraceableHitChain outputPath : trace.getHitChains(in.deviceId())) {
Andrea Campanella54923d62018-01-23 12:46:04 +0100429
pierventre4fd3b462020-10-15 17:31:44 +0200430 ConnectPoint cp = outputPath.outputPort();
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100431 log.debug("Connect point in {}", in);
Andrea Campanella54923d62018-01-23 12:46:04 +0100432 log.debug("Output path {}", cp);
pierventre4fd3b462020-10-15 17:31:44 +0200433 log.debug("{}", outputPath.egressPacket());
pierventrefe57fda2020-08-04 22:52:02 +0200434
435 if (outputPath.isDropped()) {
436 continue;
437 }
Andrea Campanella54923d62018-01-23 12:46:04 +0100438
Andrea Campanellae4084402017-12-15 15:27:31 +0100439 //Hosts for the the given output
Seyeon Jeong83e79862020-02-28 01:17:34 -0800440 Set<Host> hostsList = hostNib.getConnectedHosts(cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100441 //Hosts queried from the original ip or mac
442 Set<Host> hosts = getHosts(trace);
443
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800444 if (in.equals(cp) && trace.getInitialPacket().getCriterion(Criterion.Type.VLAN_VID) != null &&
pierventre4fd3b462020-10-15 17:31:44 +0200445 outputPath.egressPacket().packet().getCriterion(Criterion.Type.VLAN_VID) != null
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800446 && ((VlanIdCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.VLAN_VID)).vlanId()
pierventre4fd3b462020-10-15 17:31:44 +0200447 .equals(((VlanIdCriterion) outputPath.egressPacket()
448 .packet().getCriterion(Criterion.Type.VLAN_VID)).vlanId())) {
pierventrefe57fda2020-08-04 22:52:02 +0200449 if (trace.getHitChains(in.deviceId()).size() == 1 &&
pierventre4fd3b462020-10-15 17:31:44 +0200450 TroubleshootUtils.computePath(completePath, trace, outputPath.outputPort())) {
Andrea Campanellafe5d8df2018-03-12 11:07:35 -0700451 trace.addResultMessage("Connect point out " + cp + " is same as initial input " + in);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700452 trace.setSuccess(false);
Andrea Campanellafe5d8df2018-03-12 11:07:35 -0700453 }
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700454 } else if (!Collections.disjoint(hostsList, hosts)) {
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700455 //If the two host collections contain the same item it means we reached the proper output
pierventrefe57fda2020-08-04 22:52:02 +0200456 log.debug("Stopping here because host is expected destination, reached through {}", completePath);
pierventre4fd3b462020-10-15 17:31:44 +0200457 if (TroubleshootUtils.computePath(completePath, trace, outputPath.outputPort())) {
Andrea Campanella696ef032018-02-27 18:03:17 +0100458 trace.addResultMessage("Reached required destination Host " + cp);
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100459 trace.setSuccess(true);
Andrea Campanella696ef032018-02-27 18:03:17 +0100460 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100461 break;
Andrea Campanella8292ba62018-01-31 16:43:23 +0100462
pierventrefe57fda2020-08-04 22:52:02 +0200463 } else if (cp.port().equals(PortNumber.CONTROLLER)) {
Andrea Campanella54923d62018-01-23 12:46:04 +0100464 //Getting the master when the packet gets sent as packet in
Seyeon Jeong83e79862020-02-28 01:17:34 -0800465 NodeId master = mastershipNib.getMasterFor(cp.deviceId());
466 // TODO if we don't need to print master node id, exclude mastership NIB which is used only here
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100467 trace.addResultMessage(PACKET_TO_CONTROLLER + " " + master.id());
pierventre4fd3b462020-10-15 17:31:44 +0200468 TroubleshootUtils.computePath(completePath, trace, outputPath.outputPort());
Seyeon Jeong83e79862020-02-28 01:17:34 -0800469 } else if (linkNib.getEgressLinks(cp).size() > 0) {
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100470 //TODO this can be optimized if we use a Tree structure for paths.
471 //if we already have outputs let's check if the one we are considering starts from one of the devices
472 // in any of the ones we have.
473 if (trace.getCompletePaths().size() > 0) {
474 ConnectPoint inputForOutput = null;
475 List<ConnectPoint> previousPath = new ArrayList<>();
476 for (List<ConnectPoint> path : trace.getCompletePaths()) {
477 for (ConnectPoint connect : path) {
478 //if the path already contains the input for the output we've found we use it
479 if (connect.equals(in)) {
480 inputForOutput = connect;
481 previousPath = path;
482 break;
483 }
484 }
485 }
Andrea Campanella3ddbadb2018-03-09 14:52:10 -0800486
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100487 //we use the pre-existing path up to the point we fork to a new output
488 if (inputForOutput != null && completePath.contains(inputForOutput)) {
489 List<ConnectPoint> temp = new ArrayList<>(previousPath);
Andrea Campanella3ddbadb2018-03-09 14:52:10 -0800490 temp = temp.subList(0, previousPath.indexOf(inputForOutput) + 1);
491 if (completePath.containsAll(temp)) {
492 completePath = temp;
493 }
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100494 }
495 }
496
Andrea Campanellae4084402017-12-15 15:27:31 +0100497 //let's add the ouput for the input
498 completePath.add(cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100499 //let's compute the links for the given output
Seyeon Jeong83e79862020-02-28 01:17:34 -0800500 Set<Link> links = linkNib.getEgressLinks(cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100501 log.debug("Egress Links {}", links);
Andrea Campanellae4084402017-12-15 15:27:31 +0100502 //For each link we trace the corresponding device
503 for (Link link : links) {
504 ConnectPoint dst = link.dst();
505 //change in-port to the dst link in port
Andrea Campanella8292ba62018-01-31 16:43:23 +0100506 Builder updatedPacket = DefaultTrafficSelector.builder();
pierventre4fd3b462020-10-15 17:31:44 +0200507 outputPath.egressPacket().packet().criteria().forEach(updatedPacket::add);
Andrea Campanellae4084402017-12-15 15:27:31 +0100508 updatedPacket.add(Criteria.matchInPort(dst.port()));
509 log.debug("DST Connect Point {}", dst);
510 //build the elements for that device
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700511 traceInDevice(trace, updatedPacket.build(), dst, isDualHomed, completePath);
Andrea Campanellae4084402017-12-15 15:27:31 +0100512 //continue the trace along the path
Andrea Campanella696ef032018-02-27 18:03:17 +0100513 getTrace(completePath, dst, trace, isDualHomed);
Andrea Campanellae4084402017-12-15 15:27:31 +0100514 }
pierventre4fd3b462020-10-15 17:31:44 +0200515 } else if (edgePortNib.isEdgePoint(outputPath.outputPort()) &&
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100516 trace.getInitialPacket().getCriterion(Criterion.Type.ETH_DST) != null &&
517 ((EthCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.ETH_DST))
518 .mac().isMulticast()) {
pierventre4fd3b462020-10-15 17:31:44 +0200519 trace.addResultMessage("Packet is multicast and reached output " + outputPath.outputPort() +
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100520 " which is enabled and is edge port");
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100521 trace.setSuccess(true);
pierventre4fd3b462020-10-15 17:31:44 +0200522 TroubleshootUtils.computePath(completePath, trace, outputPath.outputPort());
523 if (!hasOtherOutput(in.deviceId(), trace, outputPath.outputPort())) {
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100524 return trace;
525 }
Seyeon Jeong83e79862020-02-28 01:17:34 -0800526 } else if (deviceNib.getPort(cp) != null && deviceNib.getPort(cp).isEnabled()) {
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100527 EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) trace.getInitialPacket()
528 .getCriterion(Criterion.Type.ETH_TYPE);
529 //We treat as correct output only if it's not LLDP or BDDP
530 if (!(ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
Andrea Campanellac6d10fc2018-02-27 12:42:28 +0100531 && !ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
pierventre4fd3b462020-10-15 17:31:44 +0200532 if (TroubleshootUtils.computePath(completePath, trace, outputPath.outputPort())) {
Andrea Campanella08d07e12018-03-07 14:27:54 -0800533 if (hostsList.isEmpty()) {
pierventre4fd3b462020-10-15 17:31:44 +0200534 trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.egressPacket()
535 .packet().getCriterion(Criterion.Type.ETH_TYPE)).ethType() + " and reached " +
Andrea Campanella08d07e12018-03-07 14:27:54 -0800536 cp + " with no hosts connected ");
537 } else {
538 IpAddress ipAddress = null;
539 if (trace.getInitialPacket().getCriterion(Criterion.Type.IPV4_DST) != null) {
540 ipAddress = ((IPCriterion) trace.getInitialPacket()
541 .getCriterion(Criterion.Type.IPV4_DST)).ip().address();
542 } else if (trace.getInitialPacket().getCriterion(Criterion.Type.IPV6_DST) != null) {
543 ipAddress = ((IPCriterion) trace.getInitialPacket()
544 .getCriterion(Criterion.Type.IPV6_DST)).ip().address();
545 }
546 if (ipAddress != null) {
547 IpAddress finalIpAddress = ipAddress;
548 if (hostsList.stream().anyMatch(host -> host.ipAddresses().contains(finalIpAddress)) ||
Seyeon Jeong83e79862020-02-28 01:17:34 -0800549 hostNib.getHostsByIp(finalIpAddress).isEmpty()) {
Andrea Campanella08d07e12018-03-07 14:27:54 -0800550 trace.addResultMessage("Packet is " +
pierventre4fd3b462020-10-15 17:31:44 +0200551 ((EthTypeCriterion) outputPath.egressPacket().packet()
Andrea Campanella7fa8f0a2018-03-09 15:30:22 -0800552 .getCriterion(Criterion.Type.ETH_TYPE)).ethType() +
553 " and reached " + cp + " with hosts " + hostsList);
Andrea Campanella08d07e12018-03-07 14:27:54 -0800554 } else {
555 trace.addResultMessage("Wrong output " + cp + " for required destination ip " +
556 ipAddress);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700557 trace.setSuccess(false);
Andrea Campanella08d07e12018-03-07 14:27:54 -0800558 }
559 } else {
pierventre4fd3b462020-10-15 17:31:44 +0200560 trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.egressPacket()
561 .packet().getCriterion(Criterion.Type.ETH_TYPE)).ethType()
562 + " and reached " + cp + " with hosts " + hostsList);
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800563 }
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800564 }
Andrea Campanella08d07e12018-03-07 14:27:54 -0800565 trace.setSuccess(true);
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100566 }
Andrea Campanella8292ba62018-01-31 16:43:23 +0100567 }
Andrea Campanella8292ba62018-01-31 16:43:23 +0100568
569 } else {
pierventrefe57fda2020-08-04 22:52:02 +0200570 TroubleshootUtils.computePath(completePath, trace, cp);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700571 trace.setSuccess(false);
Seyeon Jeong83e79862020-02-28 01:17:34 -0800572 if (deviceNib.getPort(cp) == null) {
Jon Hall4a28fc92018-04-24 18:03:10 -0700573 //Port is not existent on device.
Andrea Campanellae24d4922018-03-26 10:39:22 -0700574 log.warn("Port {} is not available on device.", cp);
575 trace.addResultMessage("Port " + cp + "is not available on device. Packet is dropped");
576 } else {
577 //No links means that the packet gets dropped.
578 log.warn("No links out of {}", cp);
579 trace.addResultMessage("No links depart from " + cp + ". Packet is dropped");
580 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100581 }
582 }
583 return trace;
584 }
585
Andrea Campanellae6798012018-02-06 15:46:52 +0100586 /**
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100587 * Checks if the device has other outputs than the given connect point.
588 *
589 * @param inDeviceId the device
590 * @param trace the trace we are building
591 * @param cp an output connect point
592 * @return true if the device has other outputs.
593 */
594 private boolean hasOtherOutput(DeviceId inDeviceId, StaticPacketTrace trace, ConnectPoint cp) {
pierventrefe57fda2020-08-04 22:52:02 +0200595 return trace.getHitChains(inDeviceId).stream().filter(groupsInDevice ->
pierventre4fd3b462020-10-15 17:31:44 +0200596 !groupsInDevice.outputPort().equals(cp)).count() > 0;
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100597 }
598
599 /**
Andrea Campanellae4084402017-12-15 15:27:31 +0100600 * Checks if the path contains the device.
601 *
602 * @param completePath the path
603 * @param deviceId the device to check
604 * @return true if the path contains the device
605 */
606 //TODO might prove costly, improvement: a class with both CPs and DeviceIds point.
607 private boolean pathContainsDevice(List<ConnectPoint> completePath, DeviceId deviceId) {
608 for (ConnectPoint cp : completePath) {
609 if (cp.deviceId().equals(deviceId)) {
610 return true;
611 }
612 }
613 return false;
614 }
615
616 /**
617 * Gets the hosts for the given initial packet.
618 *
619 * @param trace the trace we are building
620 * @return set of the hosts we are trying to reach
621 */
622 private Set<Host> getHosts(StaticPacketTrace trace) {
623 IPCriterion ipv4Criterion = ((IPCriterion) trace.getInitialPacket()
624 .getCriterion(Criterion.Type.IPV4_DST));
625 IPCriterion ipv6Criterion = ((IPCriterion) trace.getInitialPacket()
626 .getCriterion(Criterion.Type.IPV6_DST));
627 Set<Host> hosts = new HashSet<>();
628 if (ipv4Criterion != null) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800629 hosts.addAll(hostNib.getHostsByIp(ipv4Criterion.ip().address()));
Andrea Campanellae4084402017-12-15 15:27:31 +0100630 }
631 if (ipv6Criterion != null) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800632 hosts.addAll(hostNib.getHostsByIp(ipv6Criterion.ip().address()));
Andrea Campanellae4084402017-12-15 15:27:31 +0100633 }
634 EthCriterion ethCriterion = ((EthCriterion) trace.getInitialPacket()
635 .getCriterion(Criterion.Type.ETH_DST));
636 if (ethCriterion != null) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800637 hosts.addAll(hostNib.getHostsByMac(ethCriterion.mac()));
Andrea Campanellae4084402017-12-15 15:27:31 +0100638 }
639 return hosts;
640 }
641
642 /**
Andrea Campanellae4084402017-12-15 15:27:31 +0100643 * Traces the packet inside a device starting from an input connect point.
644 *
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700645 * @param trace the trace we are building
646 * @param packet the packet we are tracing
647 * @param in the input connect point.
648 * @param isDualHomed true if the trace we are doing starts or ends in a dual homed host
649 * @param completePath the path up until this device
Andrea Campanellae4084402017-12-15 15:27:31 +0100650 * @return updated trace
651 */
Andrea Campanella696ef032018-02-27 18:03:17 +0100652 private StaticPacketTrace traceInDevice(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in,
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700653 boolean isDualHomed, List<ConnectPoint> completePath) {
pierventrefe57fda2020-08-04 22:52:02 +0200654 // Get the behavior - do not proceed if there is no PipelineMatchable for the given device
655 PipelineTraceable pipelineMatchable = getPipelineMatchable(in.deviceId());
656 if (pipelineMatchable == null) {
657 trace.addResultMessage("No PipelineMatchable behavior for " + in.deviceId() + ". Aborting");
658 TroubleshootUtils.computePath(completePath, trace, null);
659 trace.setSuccess(false);
660 return trace;
661 }
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100662
pierventrefe57fda2020-08-04 22:52:02 +0200663 // Verify the presence of multiple routes - if the device has been visited in the past
Andrea Campanella08d07e12018-03-07 14:27:54 -0800664 boolean multipleRoutes = false;
pierventrefe57fda2020-08-04 22:52:02 +0200665 if (trace.getHitChains(in.deviceId()) != null) {
Andrea Campanella08d07e12018-03-07 14:27:54 -0800666 multipleRoutes = multipleRoutes(trace);
667 }
pierventrefe57fda2020-08-04 22:52:02 +0200668 if (trace.getHitChains(in.deviceId()) != null && !isDualHomed && !multipleRoutes) {
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100669 log.debug("Trace already contains device and given outputs");
670 return trace;
671 }
Andrea Campanella08d07e12018-03-07 14:27:54 -0800672
Andrea Campanellae4084402017-12-15 15:27:31 +0100673 log.debug("Packet {} coming in from {}", packet, in);
Andrea Campanella17d45192018-01-18 17:11:42 +0100674
675 //if device is not available exit here.
Seyeon Jeong83e79862020-02-28 01:17:34 -0800676 if (!deviceNib.isAvailable(in.deviceId())) {
Andrea Campanella17d45192018-01-18 17:11:42 +0100677 trace.addResultMessage("Device is offline " + in.deviceId());
pierventrefe57fda2020-08-04 22:52:02 +0200678 TroubleshootUtils.computePath(completePath, trace, null);
Andrea Campanella17d45192018-01-18 17:11:42 +0100679 return trace;
680 }
681
pierventrefe57fda2020-08-04 22:52:02 +0200682 // Handle when the input is the controller.
683 // NOTE, we are using the input port as a convenience to carry the CONTROLLER port number even if
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100684 // a packet in from the controller will not actually traverse the pipeline and have no such notion
685 // as the input port.
686 if (in.port().equals(PortNumber.CONTROLLER)) {
687 StaticPacketTrace outputTrace = inputFromController(trace, in);
688 if (outputTrace != null) {
689 return trace;
690 }
691 }
692
pierventrefe57fda2020-08-04 22:52:02 +0200693 // Get the device state in the form of DataPlaneEntity objects - do not proceed if there is no state
694 List<DataPlaneEntity> dataPlaneEntities = getDataPlaneEntities(in.deviceId());
695 if (dataPlaneEntities.isEmpty()) {
696 trace.addResultMessage("No device state for " + in.deviceId() + ". Aborting");
697 TroubleshootUtils.computePath(completePath, trace, null);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700698 trace.setSuccess(false);
Andrea Campanellae4084402017-12-15 15:27:31 +0100699 return trace;
700 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100701
pierventrefe57fda2020-08-04 22:52:02 +0200702 // Applies pipeline processing
pierventre4fd3b462020-10-15 17:31:44 +0200703 PipelineTraceableInput input = new PipelineTraceableInput(new PipelineTraceablePacket(packet),
704 in, dataPlaneEntities);
pierventrefe57fda2020-08-04 22:52:02 +0200705 PipelineTraceableOutput output = pipelineMatchable.apply(input);
Andrea Campanellae4084402017-12-15 15:27:31 +0100706
pierventrefe57fda2020-08-04 22:52:02 +0200707 // Update the trace
pierventre4fd3b462020-10-15 17:31:44 +0200708 List<PipelineTraceableHitChain> hitChains = output.hitChains();
pierventrefe57fda2020-08-04 22:52:02 +0200709 hitChains.forEach(hitChain -> trace.addHitChain(in.deviceId(), hitChain));
pierventre4fd3b462020-10-15 17:31:44 +0200710 trace.addResultMessage(output.log());
Andrea Campanellae4084402017-12-15 15:27:31 +0100711
pierventrefe57fda2020-08-04 22:52:02 +0200712 // If there was an error set the success to false
pierventre4fd3b462020-10-15 17:31:44 +0200713 if (output.result() != PipelineTraceableResult.SUCCESS) {
pierventrefe57fda2020-08-04 22:52:02 +0200714 TroubleshootUtils.computePath(completePath, trace, null);
715 trace.setSuccess(false);
Andrea Campanellae4084402017-12-15 15:27:31 +0100716 }
717
pierventrefe57fda2020-08-04 22:52:02 +0200718 // We are done!
Andrea Campanella08d07e12018-03-07 14:27:54 -0800719 return trace;
720 }
721
pierventrefe57fda2020-08-04 22:52:02 +0200722 // Compute whether or not there are multiple routes.
Andrea Campanella08d07e12018-03-07 14:27:54 -0800723 private boolean multipleRoutes(StaticPacketTrace trace) {
724 boolean multipleRoutes = false;
725 IPCriterion ipCriterion = ((IPCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.IPV4_DST));
726 IpAddress ip = null;
727 if (ipCriterion != null) {
728 ip = ipCriterion.ip().address();
729 } else if (trace.getInitialPacket().getCriterion(Criterion.Type.IPV6_DST) != null) {
730 ip = ((IPCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.IPV6_DST)).ip().address();
Andrea Campanella54923d62018-01-23 12:46:04 +0100731 }
Andrea Campanelladd71aca2018-04-04 13:10:45 +0200732 if (ip != null) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800733 Optional<ResolvedRoute> optionalRoute = routeNib.longestPrefixLookup(ip);
Andrea Campanelladd71aca2018-04-04 13:10:45 +0200734 if (optionalRoute.isPresent()) {
735 ResolvedRoute route = optionalRoute.get();
Seyeon Jeong83e79862020-02-28 01:17:34 -0800736 multipleRoutes = routeNib.getAllResolvedRoutes(route.prefix()).size() > 1;
Andrea Campanelladd71aca2018-04-04 13:10:45 +0200737 }
Andrea Campanella8292ba62018-01-31 16:43:23 +0100738 }
Andrea Campanella08d07e12018-03-07 14:27:54 -0800739 return multipleRoutes;
Andrea Campanellae4084402017-12-15 15:27:31 +0100740 }
741
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100742 /**
743 * Handles the specific case where the Input is the controller.
744 * Note that the in port is used as a convenience to store the port of the controller even if the packet in
745 * from a controller should not have a physical input port. The in port from the Controller is used to make sure
746 * the flood to all active physical ports of the device.
747 *
748 * @param trace the trace
749 * @param in the controller port
750 * @return the augmented trace.
751 */
752 private StaticPacketTrace inputFromController(StaticPacketTrace trace, ConnectPoint in) {
753 EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) trace.getInitialPacket()
754 .getCriterion(Criterion.Type.ETH_TYPE);
755 //If the packet is LLDP or BDDP we flood it on all active ports of the switch.
756 if (ethTypeCriterion != null && (ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
757 || ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
758 //get the active ports
Seyeon Jeong83e79862020-02-28 01:17:34 -0800759 List<Port> enabledPorts = deviceNib.getPorts(in.deviceId()).stream()
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100760 .filter(Port::isEnabled)
761 .collect(Collectors.toList());
762 //build an output from each one
763 enabledPorts.forEach(port -> {
pierventrefe57fda2020-08-04 22:52:02 +0200764 PipelineTraceableHitChain hitChain = new PipelineTraceableHitChain(
765 new ConnectPoint(port.element().id(), port.number()), ImmutableList.of(),
pierventre4fd3b462020-10-15 17:31:44 +0200766 new PipelineTraceablePacket(trace.getInitialPacket()));
pierventrefe57fda2020-08-04 22:52:02 +0200767 trace.addHitChain(in.deviceId(), hitChain);
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100768 });
769 return trace;
770 }
771 return null;
772 }
773
pierventrefe57fda2020-08-04 22:52:02 +0200774 ////////////////////////////////
775 // Cemetery - Deprecated code //
776 ////////////////////////////////
777 @Override
778 public List<StaticPacketTrace> pingAll(EtherType type) {
779 ImmutableList.Builder<StaticPacketTrace> tracesBuilder = ImmutableList.builder();
780 hostNib.getHosts().forEach(host -> {
781 List<IpAddress> ipAddresses = getIpAddresses(host, type, false);
782 if (ipAddresses.size() > 0) {
783 //check if the host has only local IPs of that ETH type
784 boolean onlyLocalSrc = ipAddresses.size() == 1 && ipAddresses.get(0).isLinkLocal();
785 hostNib.getHosts().forEach(hostToPing -> {
786 List<IpAddress> ipAddressesToPing = getIpAddresses(hostToPing, type, false);
787 //check if the other host has only local IPs of that ETH type
788 boolean onlyLocalDst = ipAddressesToPing.size() == 1 && ipAddressesToPing.get(0).isLinkLocal();
789 boolean sameLocation = Sets.intersection(host.locations(), hostToPing.locations()).size() > 0;
790 //Trace is done only if they are both local and under the same location
791 // or not local and if they are not the same host.
792 if (((sameLocation && onlyLocalDst && onlyLocalSrc) ||
793 (!onlyLocalSrc && !onlyLocalDst && ipAddressesToPing.size() > 0))
794 && !host.equals(hostToPing)) {
795 tracesBuilder.addAll(trace(host.id(), hostToPing.id(), type));
796 }
797 });
Andrea Campanellae4084402017-12-15 15:27:31 +0100798 }
799 });
pierventrefe57fda2020-08-04 22:52:02 +0200800 return tracesBuilder.build();
Simon Hunt026a2872017-11-13 17:09:43 -0800801 }
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100802
pierventrefe57fda2020-08-04 22:52:02 +0200803 @Override
804 public List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId) {
805 Generator<Set<StaticPacketTrace>> gen = new McastGenerator(mcastRouteNib, this, vlanId);
806 List<Set<StaticPacketTrace>> multicastTraceList =
807 StreamSupport.stream(gen.spliterator(), false).collect(Collectors.toList());
808 return multicastTraceList;
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100809 }
810
Simon Hunt026a2872017-11-13 17:09:43 -0800811}