blob: b683351d11537c9aa8ba54f1e5154611a7481817 [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;
Andrea Campanella1445f7a2018-02-07 12:00:12 +010043import org.onosproject.net.Port;
Andrea Campanellae4084402017-12-15 15:27:31 +010044import org.onosproject.net.PortNumber;
pierventrefe57fda2020-08-04 22:52:02 +020045import org.onosproject.net.behaviour.PipelineTraceable;
Andrea Campanella55c3f422018-02-08 17:10:11 +010046import org.onosproject.net.config.ConfigException;
Andrea Campanella55c3f422018-02-08 17:10:11 +010047import org.onosproject.net.config.basics.InterfaceConfig;
Andrea Campanellae4084402017-12-15 15:27:31 +010048import org.onosproject.net.flow.DefaultTrafficSelector;
49import org.onosproject.net.flow.FlowEntry;
Simon Hunt026a2872017-11-13 17:09:43 -080050import org.onosproject.net.flow.TrafficSelector;
Andrea Campanellae4084402017-12-15 15:27:31 +010051import org.onosproject.net.flow.criteria.Criteria;
52import org.onosproject.net.flow.criteria.Criterion;
53import org.onosproject.net.flow.criteria.EthCriterion;
54import org.onosproject.net.flow.criteria.EthTypeCriterion;
55import org.onosproject.net.flow.criteria.IPCriterion;
Andrea Campanellae6798012018-02-06 15:46:52 +010056import org.onosproject.net.flow.criteria.VlanIdCriterion;
Andrea Campanellae4084402017-12-15 15:27:31 +010057import org.onosproject.net.group.Group;
Andrea Campanella55c3f422018-02-08 17:10:11 +010058import org.onosproject.net.host.InterfaceIpAddress;
59import org.onosproject.net.intf.Interface;
Andrea Campanella08d07e12018-03-07 14:27:54 -080060import org.onosproject.routeservice.ResolvedRoute;
Andrea Campanella55c3f422018-02-08 17:10:11 +010061import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
Seyeon Jeong83e79862020-02-28 01:17:34 -080062import org.onosproject.t3.api.DeviceNib;
63import org.onosproject.t3.api.DriverNib;
64import org.onosproject.t3.api.EdgePortNib;
65import org.onosproject.t3.api.FlowNib;
66import org.onosproject.t3.api.GroupNib;
Seyeon Jeong83e79862020-02-28 01:17:34 -080067import org.onosproject.t3.api.HostNib;
68import org.onosproject.t3.api.LinkNib;
69import org.onosproject.t3.api.MastershipNib;
70import org.onosproject.t3.api.MulticastRouteNib;
71import org.onosproject.t3.api.NetworkConfigNib;
Seyeon Jeong5018bdd2020-02-28 01:17:34 -080072import org.onosproject.t3.api.NibProfile;
Seyeon Jeong83e79862020-02-28 01:17:34 -080073import org.onosproject.t3.api.RouteNib;
Simon Hunt026a2872017-11-13 17:09:43 -080074import org.onosproject.t3.api.StaticPacketTrace;
75import org.onosproject.t3.api.TroubleshootService;
Ray Milkey9511b512018-08-17 14:54:17 -070076import org.osgi.service.component.annotations.Component;
Simon Hunt026a2872017-11-13 17:09:43 -080077import org.slf4j.Logger;
78
pierventrefe57fda2020-08-04 22:52:02 +020079import java.util.Map;
Andrea Campanellae4084402017-12-15 15:27:31 +010080import java.util.List;
81import java.util.Set;
pierventrefe57fda2020-08-04 22:52:02 +020082import java.util.HashSet;
83import java.util.Optional;
84import java.util.ArrayList;
85import java.util.Collections;
86
Andrea Campanellae4084402017-12-15 15:27:31 +010087import java.util.stream.Collectors;
Seyeon Jeong83e79862020-02-28 01:17:34 -080088import java.util.stream.Stream;
psnehab823c512018-08-02 07:41:43 -040089import java.util.stream.StreamSupport;
Andrea Campanellae4084402017-12-15 15:27:31 +010090
91import static org.onlab.packet.EthType.EtherType;
Andrea Campanellae6798012018-02-06 15:46:52 +010092import static org.onosproject.net.flow.TrafficSelector.Builder;
Simon Hunt026a2872017-11-13 17:09:43 -080093import static org.slf4j.LoggerFactory.getLogger;
94
95/**
Andrea Campanellae4084402017-12-15 15:27:31 +010096 * Manager to troubleshoot packets inside the network.
97 * Given a representation of a packet follows it's path in the network according to the existing flows and groups in
98 * the devices.
Simon Hunt026a2872017-11-13 17:09:43 -080099 */
Ray Milkey9511b512018-08-17 14:54:17 -0700100@Component(immediate = true, service = TroubleshootService.class)
Simon Hunt026a2872017-11-13 17:09:43 -0800101public class TroubleshootManager implements TroubleshootService {
102
103 private static final Logger log = getLogger(TroubleshootManager.class);
104
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100105 static final String PACKET_TO_CONTROLLER = "Packet goes to the controller";
106
Seyeon Jeong83e79862020-02-28 01:17:34 -0800107 // uses a snapshot (cache) of NIBs instead of interacting with ONOS core in runtime
Seyeon Jeong5018bdd2020-02-28 01:17:34 -0800108 protected FlowNib flowNib = FlowNib.getInstance();
109 protected GroupNib groupNib = GroupNib.getInstance();
110 protected LinkNib linkNib = LinkNib.getInstance();
111 protected HostNib hostNib = HostNib.getInstance();
112 protected DeviceNib deviceNib = DeviceNib.getInstance();
113 protected DriverNib driverNib = DriverNib.getInstance();
114 protected MastershipNib mastershipNib = MastershipNib.getInstance();
115 protected EdgePortNib edgePortNib = EdgePortNib.getInstance();
116 protected RouteNib routeNib = RouteNib.getInstance();
117 protected NetworkConfigNib networkConfigNib = NetworkConfigNib.getInstance();
118 protected MulticastRouteNib mcastRouteNib = MulticastRouteNib.getInstance();
Simon Hunt026a2872017-11-13 17:09:43 -0800119
pierventrefe57fda2020-08-04 22:52:02 +0200120 // FIXME Revisit offline mode after a first implementation
121 private final Map<DeviceId, PipelineTraceable> pipelineTraceables = Maps.newConcurrentMap();
122
Seyeon Jeong83e79862020-02-28 01:17:34 -0800123 @Override
Seyeon Jeong5018bdd2020-02-28 01:17:34 -0800124 public boolean checkNibValidity() {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800125 return Stream.of(flowNib, groupNib, linkNib, hostNib, deviceNib, driverNib,
126 mastershipNib, edgePortNib, routeNib, networkConfigNib, mcastRouteNib)
Seyeon Jeong5018bdd2020-02-28 01:17:34 -0800127 .allMatch(nib -> nib != null && nib.isValid());
Seyeon Jeong83e79862020-02-28 01:17:34 -0800128 }
Simon Hunt026a2872017-11-13 17:09:43 -0800129
Seyeon Jeong83e79862020-02-28 01:17:34 -0800130 @Override
Seyeon Jeong5018bdd2020-02-28 01:17:34 -0800131 public String printNibSummary() {
132 StringBuilder summary = new StringBuilder().append("*** Current NIB in valid: ***\n");
133 Stream.of(flowNib, groupNib, linkNib, hostNib, deviceNib, driverNib,
134 mastershipNib, edgePortNib, routeNib, networkConfigNib, mcastRouteNib)
135 .forEach(nib -> {
136 NibProfile profile = nib.getProfile();
137 summary.append(String.format(
138 nib.getClass().getName() + " created %s from %s\n",
139 profile.date(), profile.sourceType()));
140 });
141
142 return summary.append(StringUtils.rightPad("", 125, '-')).toString();
Seyeon Jeong83e79862020-02-28 01:17:34 -0800143 }
Andrea Campanellabd15bf52018-04-06 16:30:18 +0200144
Andrea Campanella55c3f422018-02-08 17:10:11 +0100145 @Override
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100146 public Generator<Set<StaticPacketTrace>> pingAllGenerator(EtherType type) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800147 return new PingAllGenerator(type, hostNib, this);
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100148 }
149
150 @Override
Andrea Campanellabd15bf52018-04-06 16:30:18 +0200151 public Generator<Set<StaticPacketTrace>> traceMcast(VlanId vlanId) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800152 return new McastGenerator(mcastRouteNib, this, vlanId);
Andrea Campanellabd15bf52018-04-06 16:30:18 +0200153 }
154
155 @Override
Andrea Campanella696ef032018-02-27 18:03:17 +0100156 public Set<StaticPacketTrace> trace(HostId sourceHost, HostId destinationHost, EtherType etherType) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800157 Host source = hostNib.getHost(sourceHost);
158 Host destination = hostNib.getHost(destinationHost);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100159
Andrea Campanella6a614fa2018-02-21 14:28:20 +0100160 //Temporary trace to fail in case we don't have enough information or what is provided is incoherent
161 StaticPacketTrace failTrace = new StaticPacketTrace(null, null, Pair.of(source, destination));
Andrea Campanella55c3f422018-02-08 17:10:11 +0100162
163 if (source == null) {
164 failTrace.addResultMessage("Source Host " + sourceHost + " does not exist");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700165 failTrace.setSuccess(false);
166
Andrea Campanella696ef032018-02-27 18:03:17 +0100167 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100168 }
169
170 if (destination == null) {
171 failTrace.addResultMessage("Destination Host " + destinationHost + " does not exist");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700172 failTrace.setSuccess(false);
Andrea Campanella696ef032018-02-27 18:03:17 +0100173 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100174 }
175
176 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
Andrea Campanella55c3f422018-02-08 17:10:11 +0100177 .matchEthType(etherType.ethType().toShort())
178 .matchEthDst(source.mac())
179 .matchVlanId(source.vlan());
180
181
Andrea Campanella55c3f422018-02-08 17:10:11 +0100182 try {
Andrea Campanellae24d4922018-03-26 10:39:22 -0700183 ImmutableSet.Builder<StaticPacketTrace> traces = ImmutableSet.builder();
Andrea Campanella55c3f422018-02-08 17:10:11 +0100184 //if the location deviceId is the same, the two hosts are under same subnet and vlan on the interface
185 // we are under same leaf so it's L2 Unicast.
186 if (areBridged(source, destination)) {
187 selectorBuilder.matchEthDst(destination.mac());
Andrea Campanella696ef032018-02-27 18:03:17 +0100188 source.locations().forEach(hostLocation -> {
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800189 selectorBuilder.matchInPort(hostLocation.port());
Andrea Campanella696ef032018-02-27 18:03:17 +0100190 StaticPacketTrace trace = trace(selectorBuilder.build(), hostLocation);
191 trace.addEndpointHosts(Pair.of(source, destination));
192 traces.add(trace);
193 });
Andrea Campanellae24d4922018-03-26 10:39:22 -0700194 //The destination host is not dual homed, if it is the other path might be done through routing.
195 if (destination.locations().size() == 1) {
196 return traces.build();
197 }
Andrea Campanella55c3f422018-02-08 17:10:11 +0100198 }
Andrea Campanella55c3f422018-02-08 17:10:11 +0100199 //handle the IPs for src and dst in case of L3
200 if (etherType.equals(EtherType.IPV4) || etherType.equals(EtherType.IPV6)) {
201
202 //Match on the source IP
203 if (!matchIP(source, failTrace, selectorBuilder, etherType, true)) {
Andrea Campanella696ef032018-02-27 18:03:17 +0100204 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100205 }
206
207 //Match on destination IP
208 if (!matchIP(destination, failTrace, selectorBuilder, etherType, false)) {
Andrea Campanella696ef032018-02-27 18:03:17 +0100209 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100210 }
211
212 } else {
213 failTrace.addResultMessage("Host based trace supports only IPv4 or IPv6 as EtherType, " +
214 "please use packet based");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700215 failTrace.setSuccess(false);
Andrea Campanella696ef032018-02-27 18:03:17 +0100216 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100217 }
218
219 //l3 unicast, we get the dst mac of the leaf the source is connected to from netcfg
Seyeon Jeong83e79862020-02-28 01:17:34 -0800220 SegmentRoutingDeviceConfig segmentRoutingConfig = networkConfigNib.getConfig(source.location()
Andrea Campanella55c3f422018-02-08 17:10:11 +0100221 .deviceId(), SegmentRoutingDeviceConfig.class);
222 if (segmentRoutingConfig != null) {
223 selectorBuilder.matchEthDst(segmentRoutingConfig.routerMac());
224 } else {
225 failTrace.addResultMessage("Can't get " + source.location().deviceId() +
226 " router MAC from segment routing config can't perform L3 tracing.");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700227 failTrace.setSuccess(false);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100228 }
Andrea Campanella696ef032018-02-27 18:03:17 +0100229 source.locations().forEach(hostLocation -> {
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800230 selectorBuilder.matchInPort(hostLocation.port());
Andrea Campanella696ef032018-02-27 18:03:17 +0100231 StaticPacketTrace trace = trace(selectorBuilder.build(), hostLocation);
232 trace.addEndpointHosts(Pair.of(source, destination));
233 traces.add(trace);
234 });
235 return traces.build();
Andrea Campanella55c3f422018-02-08 17:10:11 +0100236
237 } catch (ConfigException e) {
238 failTrace.addResultMessage("Can't get config " + e.getMessage());
Andrea Campanella696ef032018-02-27 18:03:17 +0100239 return ImmutableSet.of(failTrace);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100240 }
241 }
242
pierventrefe57fda2020-08-04 22:52:02 +0200243 private PipelineTraceable getPipelineMatchable(DeviceId deviceId) {
244 return pipelineTraceables.compute(deviceId, (k, v) -> {
245 if (v == null) {
246 log.info("PipelineMatchable not found for {}", deviceId);
247 Device d = deviceNib.getDevice(deviceId);
248 if (d.is(PipelineTraceable.class)) {
249 v = d.as(PipelineTraceable.class);
250 v.init();
251 } else {
252 log.warn("PipelineMatchable behaviour not supported for device {}",
253 deviceId);
254 }
255 }
256 return v;
257 });
258 }
259
260 private List<DataPlaneEntity> getDataPlaneEntities(DeviceId deviceId) {
261 List<DataPlaneEntity> dataPlaneEntities = Lists.newArrayList();
262 flowNib.getFlowEntriesByState(deviceId, FlowEntry.FlowEntryState.ADDED).forEach(entity ->
263 dataPlaneEntities.add(new DataPlaneEntity(entity)));
264 groupNib.getGroupsByState(deviceId, Group.GroupState.ADDED).forEach(entity ->
265 dataPlaneEntities.add(new DataPlaneEntity(entity)));
266 return dataPlaneEntities;
267 }
268
Andrea Campanella55c3f422018-02-08 17:10:11 +0100269 /**
270 * Matches src and dst IPs based on host information.
271 *
272 * @param host the host
273 * @param failTrace the trace to use in case of failure
274 * @param selectorBuilder the packet we are building to trace
275 * @param etherType the traffic type
276 * @param src is this src host or dst host
277 * @return true if properly matched
278 */
279 private boolean matchIP(Host host, StaticPacketTrace failTrace, Builder selectorBuilder,
280 EtherType etherType, boolean src) {
Andrea Campanella6a614fa2018-02-21 14:28:20 +0100281 List<IpAddress> ips = getIpAddresses(host, etherType, true);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100282
283 if (ips.size() > 0) {
Andrea Campanellabf8d9302018-02-21 14:43:21 +0100284 if (etherType.equals(EtherType.IPV4)) {
285 if (src) {
286 selectorBuilder.matchIPSrc(ips.get(0).toIpPrefix());
287 } else {
288 selectorBuilder.matchIPDst(ips.get(0).toIpPrefix());
289 }
290 } else if (etherType.equals(EtherType.IPV6)) {
291 if (src) {
292 selectorBuilder.matchIPv6Src(ips.get(0).toIpPrefix());
293 } else {
294 selectorBuilder.matchIPv6Dst(ips.get(0).toIpPrefix());
295 }
Andrea Campanella55c3f422018-02-08 17:10:11 +0100296 }
297 } else {
298 failTrace.addResultMessage("Host " + host + " has no " + etherType + " address");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700299 failTrace.setSuccess(false);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100300 return false;
301 }
302 return true;
303 }
304
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100305 List<IpAddress> getIpAddresses(Host host, EtherType etherType, boolean checklocal) {
Andrea Campanella6a614fa2018-02-21 14:28:20 +0100306 return host.ipAddresses().stream().filter(ipAddress -> {
307 boolean correctIp = false;
308 if (etherType.equals(EtherType.IPV4)) {
309 correctIp = ipAddress.isIp4();
310 } else if (etherType.equals(EtherType.IPV6)) {
311 correctIp = ipAddress.isIp6();
312 }
313 if (checklocal) {
314 correctIp = correctIp && !ipAddress.isLinkLocal();
315 }
316 return correctIp;
317 }).collect(Collectors.toList());
318 }
319
Andrea Campanella55c3f422018-02-08 17:10:11 +0100320 /**
321 * Checks that two hosts are bridged (L2Unicast).
322 *
323 * @param source the source host
324 * @param destination the destination host
325 * @return true if bridged.
326 * @throws ConfigException if config can't be properly retrieved
327 */
328 private boolean areBridged(Host source, Host destination) throws ConfigException {
329
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800330 //If the locations is not the same we don't even check vlan or subnets
331 if (Collections.disjoint(source.locations(), destination.locations())) {
332 return false;
333 }
334
335 if (!source.vlan().equals(VlanId.NONE) && !destination.vlan().equals(VlanId.NONE)
336 && !source.vlan().equals(destination.vlan())) {
Andrea Campanella55c3f422018-02-08 17:10:11 +0100337 return false;
338 }
339
Seyeon Jeong83e79862020-02-28 01:17:34 -0800340 InterfaceConfig interfaceCfgH1 = networkConfigNib.getConfig(source.location(), InterfaceConfig.class);
341 InterfaceConfig interfaceCfgH2 = networkConfigNib.getConfig(destination.location(), InterfaceConfig.class);
Andrea Campanella55c3f422018-02-08 17:10:11 +0100342 if (interfaceCfgH1 != null && interfaceCfgH2 != null) {
343
344 //following can be optimized but for clarity is left as is
345 Interface intfH1 = interfaceCfgH1.getInterfaces().stream().findFirst().get();
346 Interface intfH2 = interfaceCfgH2.getInterfaces().stream().findFirst().get();
347
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800348 if (source.vlan().equals(VlanId.NONE) && !destination.vlan().equals(VlanId.NONE)) {
349 return intfH1.vlanUntagged().equals(destination.vlan()) ||
350 intfH1.vlanNative().equals(destination.vlan());
Andrea Campanella55c3f422018-02-08 17:10:11 +0100351 }
352
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800353 if (!source.vlan().equals(VlanId.NONE) && destination.vlan().equals(VlanId.NONE)) {
354 return intfH2.vlanUntagged().equals(source.vlan()) ||
355 intfH2.vlanNative().equals(source.vlan());
356 }
357
358 if (!intfH1.vlanNative().equals(intfH2.vlanNative())) {
Andrea Campanella55c3f422018-02-08 17:10:11 +0100359 return false;
360 }
361
362 if (!intfH1.vlanUntagged().equals(intfH2.vlanUntagged())) {
363 return false;
364 }
365
366 List<InterfaceIpAddress> intersection = new ArrayList<>(intfH1.ipAddressesList());
367 intersection.retainAll(intfH2.ipAddressesList());
368 if (intersection.size() == 0) {
369 return false;
370 }
371 }
372 return true;
373 }
374
Simon Hunt026a2872017-11-13 17:09:43 -0800375 @Override
376 public StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in) {
Andrea Campanellae4084402017-12-15 15:27:31 +0100377 log.info("Tracing packet {} coming in through {}", packet, in);
Andrea Campanella17d45192018-01-18 17:11:42 +0100378 //device must exist in ONOS
Seyeon Jeong83e79862020-02-28 01:17:34 -0800379 Preconditions.checkNotNull(deviceNib.getDevice(in.deviceId()),
Andrea Campanella17d45192018-01-18 17:11:42 +0100380 "Device " + in.deviceId() + " must exist in ONOS");
381
Andrea Campanellae4084402017-12-15 15:27:31 +0100382 StaticPacketTrace trace = new StaticPacketTrace(packet, in);
Andrea Campanella696ef032018-02-27 18:03:17 +0100383 boolean isDualHomed = getHosts(trace).stream().anyMatch(host -> host.locations().size() > 1);
Andrea Campanellae4084402017-12-15 15:27:31 +0100384 //FIXME this can be done recursively
Andrea Campanellae4084402017-12-15 15:27:31 +0100385 //Building output connect Points
386 List<ConnectPoint> path = new ArrayList<>();
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700387 trace = traceInDevice(trace, packet, in, isDualHomed, path);
Andrea Campanella696ef032018-02-27 18:03:17 +0100388 trace = getTrace(path, in, trace, isDualHomed);
Andrea Campanellae4084402017-12-15 15:27:31 +0100389 return trace;
390 }
391
392 /**
393 * Computes a trace for a give packet that start in the network at the given connect point.
394 *
395 * @param completePath the path traversed by the packet
396 * @param in the input connect point
397 * @param trace the trace to build
Andrea Campanella696ef032018-02-27 18:03:17 +0100398 * @param isDualHomed true if the trace we are doing starts or ends in a dual homed host
Andrea Campanellae4084402017-12-15 15:27:31 +0100399 * @return the build trace for that packet.
400 */
Andrea Campanella696ef032018-02-27 18:03:17 +0100401 private StaticPacketTrace getTrace(List<ConnectPoint> completePath, ConnectPoint in, StaticPacketTrace trace,
402 boolean isDualHomed) {
Andrea Campanellae4084402017-12-15 15:27:31 +0100403
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100404 log.debug("------------------------------------------------------------");
405
Andrea Campanellae4084402017-12-15 15:27:31 +0100406 //if the trace already contains the input connect point there is a loop
407 if (pathContainsDevice(completePath, in.deviceId())) {
408 trace.addResultMessage("Loop encountered in device " + in.deviceId());
Andrea Campanella3ddbadb2018-03-09 14:52:10 -0800409 completePath.add(in);
410 trace.addCompletePath(completePath);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700411 trace.setSuccess(false);
Andrea Campanellae4084402017-12-15 15:27:31 +0100412 return trace;
413 }
414
415 //let's add the input connect point
416 completePath.add(in);
417
418 //If the trace has no outputs for the given input we stop here
pierventrefe57fda2020-08-04 22:52:02 +0200419 if (trace.getHitChains(in.deviceId()) == null) {
420 TroubleshootUtils.computePath(completePath, trace, null);
Andrea Campanellae4084402017-12-15 15:27:31 +0100421 trace.addResultMessage("No output out of device " + in.deviceId() + ". Packet is dropped");
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700422 trace.setSuccess(false);
Andrea Campanellae4084402017-12-15 15:27:31 +0100423 return trace;
424 }
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100425
Andrea Campanella27140da2018-04-09 14:22:32 +0200426 //If the trace has outputs we analyze them all
pierventrefe57fda2020-08-04 22:52:02 +0200427 for (PipelineTraceableHitChain outputPath : trace.getHitChains(in.deviceId())) {
Andrea Campanella54923d62018-01-23 12:46:04 +0100428
pierventrefe57fda2020-08-04 22:52:02 +0200429 ConnectPoint cp = outputPath.getOutputPort();
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100430 log.debug("Connect point in {}", in);
Andrea Campanella54923d62018-01-23 12:46:04 +0100431 log.debug("Output path {}", cp);
pierventrefe57fda2020-08-04 22:52:02 +0200432 log.debug("{}", outputPath.getEgressPacket());
433
434 if (outputPath.isDropped()) {
435 continue;
436 }
Andrea Campanella54923d62018-01-23 12:46:04 +0100437
Andrea Campanellae4084402017-12-15 15:27:31 +0100438 //Hosts for the the given output
Seyeon Jeong83e79862020-02-28 01:17:34 -0800439 Set<Host> hostsList = hostNib.getConnectedHosts(cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100440 //Hosts queried from the original ip or mac
441 Set<Host> hosts = getHosts(trace);
442
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800443 if (in.equals(cp) && trace.getInitialPacket().getCriterion(Criterion.Type.VLAN_VID) != null &&
pierventrefe57fda2020-08-04 22:52:02 +0200444 outputPath.getEgressPacket().getCriterion(Criterion.Type.VLAN_VID) != null
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800445 && ((VlanIdCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.VLAN_VID)).vlanId()
pierventrefe57fda2020-08-04 22:52:02 +0200446 .equals(((VlanIdCriterion) outputPath.getEgressPacket().getCriterion(Criterion.Type.VLAN_VID))
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800447 .vlanId())) {
pierventrefe57fda2020-08-04 22:52:02 +0200448 if (trace.getHitChains(in.deviceId()).size() == 1 &&
449 TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort())) {
Andrea Campanellafe5d8df2018-03-12 11:07:35 -0700450 trace.addResultMessage("Connect point out " + cp + " is same as initial input " + in);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700451 trace.setSuccess(false);
Andrea Campanellafe5d8df2018-03-12 11:07:35 -0700452 }
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700453 } else if (!Collections.disjoint(hostsList, hosts)) {
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700454 //If the two host collections contain the same item it means we reached the proper output
pierventrefe57fda2020-08-04 22:52:02 +0200455 log.debug("Stopping here because host is expected destination, reached through {}", completePath);
456 if (TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort())) {
Andrea Campanella696ef032018-02-27 18:03:17 +0100457 trace.addResultMessage("Reached required destination Host " + cp);
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100458 trace.setSuccess(true);
Andrea Campanella696ef032018-02-27 18:03:17 +0100459 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100460 break;
Andrea Campanella8292ba62018-01-31 16:43:23 +0100461
pierventrefe57fda2020-08-04 22:52:02 +0200462 } else if (cp.port().equals(PortNumber.CONTROLLER)) {
Andrea Campanella54923d62018-01-23 12:46:04 +0100463 //Getting the master when the packet gets sent as packet in
Seyeon Jeong83e79862020-02-28 01:17:34 -0800464 NodeId master = mastershipNib.getMasterFor(cp.deviceId());
465 // 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 +0100466 trace.addResultMessage(PACKET_TO_CONTROLLER + " " + master.id());
pierventrefe57fda2020-08-04 22:52:02 +0200467 TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort());
Seyeon Jeong83e79862020-02-28 01:17:34 -0800468 } else if (linkNib.getEgressLinks(cp).size() > 0) {
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100469 //TODO this can be optimized if we use a Tree structure for paths.
470 //if we already have outputs let's check if the one we are considering starts from one of the devices
471 // in any of the ones we have.
472 if (trace.getCompletePaths().size() > 0) {
473 ConnectPoint inputForOutput = null;
474 List<ConnectPoint> previousPath = new ArrayList<>();
475 for (List<ConnectPoint> path : trace.getCompletePaths()) {
476 for (ConnectPoint connect : path) {
477 //if the path already contains the input for the output we've found we use it
478 if (connect.equals(in)) {
479 inputForOutput = connect;
480 previousPath = path;
481 break;
482 }
483 }
484 }
Andrea Campanella3ddbadb2018-03-09 14:52:10 -0800485
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100486 //we use the pre-existing path up to the point we fork to a new output
487 if (inputForOutput != null && completePath.contains(inputForOutput)) {
488 List<ConnectPoint> temp = new ArrayList<>(previousPath);
Andrea Campanella3ddbadb2018-03-09 14:52:10 -0800489 temp = temp.subList(0, previousPath.indexOf(inputForOutput) + 1);
490 if (completePath.containsAll(temp)) {
491 completePath = temp;
492 }
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100493 }
494 }
495
Andrea Campanellae4084402017-12-15 15:27:31 +0100496 //let's add the ouput for the input
497 completePath.add(cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100498 //let's compute the links for the given output
Seyeon Jeong83e79862020-02-28 01:17:34 -0800499 Set<Link> links = linkNib.getEgressLinks(cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100500 log.debug("Egress Links {}", links);
Andrea Campanellae4084402017-12-15 15:27:31 +0100501 //For each link we trace the corresponding device
502 for (Link link : links) {
503 ConnectPoint dst = link.dst();
504 //change in-port to the dst link in port
Andrea Campanella8292ba62018-01-31 16:43:23 +0100505 Builder updatedPacket = DefaultTrafficSelector.builder();
pierventrefe57fda2020-08-04 22:52:02 +0200506 outputPath.getEgressPacket().criteria().forEach(updatedPacket::add);
Andrea Campanellae4084402017-12-15 15:27:31 +0100507 updatedPacket.add(Criteria.matchInPort(dst.port()));
508 log.debug("DST Connect Point {}", dst);
509 //build the elements for that device
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700510 traceInDevice(trace, updatedPacket.build(), dst, isDualHomed, completePath);
Andrea Campanellae4084402017-12-15 15:27:31 +0100511 //continue the trace along the path
Andrea Campanella696ef032018-02-27 18:03:17 +0100512 getTrace(completePath, dst, trace, isDualHomed);
Andrea Campanellae4084402017-12-15 15:27:31 +0100513 }
pierventrefe57fda2020-08-04 22:52:02 +0200514 } else if (edgePortNib.isEdgePoint(outputPath.getOutputPort()) &&
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100515 trace.getInitialPacket().getCriterion(Criterion.Type.ETH_DST) != null &&
516 ((EthCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.ETH_DST))
517 .mac().isMulticast()) {
pierventrefe57fda2020-08-04 22:52:02 +0200518 trace.addResultMessage("Packet is multicast and reached output " + outputPath.getOutputPort() +
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100519 " which is enabled and is edge port");
Andrea Campanella0cc6acd2018-02-28 16:43:16 +0100520 trace.setSuccess(true);
pierventrefe57fda2020-08-04 22:52:02 +0200521 TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort());
522 if (!hasOtherOutput(in.deviceId(), trace, outputPath.getOutputPort())) {
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100523 return trace;
524 }
Seyeon Jeong83e79862020-02-28 01:17:34 -0800525 } else if (deviceNib.getPort(cp) != null && deviceNib.getPort(cp).isEnabled()) {
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100526 EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) trace.getInitialPacket()
527 .getCriterion(Criterion.Type.ETH_TYPE);
528 //We treat as correct output only if it's not LLDP or BDDP
529 if (!(ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
Andrea Campanellac6d10fc2018-02-27 12:42:28 +0100530 && !ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
pierventrefe57fda2020-08-04 22:52:02 +0200531 if (TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort())) {
Andrea Campanella08d07e12018-03-07 14:27:54 -0800532 if (hostsList.isEmpty()) {
pierventrefe57fda2020-08-04 22:52:02 +0200533 trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getEgressPacket()
Andrea Campanella08d07e12018-03-07 14:27:54 -0800534 .getCriterion(Criterion.Type.ETH_TYPE)).ethType() + " and reached " +
535 cp + " with no hosts connected ");
536 } else {
537 IpAddress ipAddress = null;
538 if (trace.getInitialPacket().getCriterion(Criterion.Type.IPV4_DST) != null) {
539 ipAddress = ((IPCriterion) trace.getInitialPacket()
540 .getCriterion(Criterion.Type.IPV4_DST)).ip().address();
541 } else if (trace.getInitialPacket().getCriterion(Criterion.Type.IPV6_DST) != null) {
542 ipAddress = ((IPCriterion) trace.getInitialPacket()
543 .getCriterion(Criterion.Type.IPV6_DST)).ip().address();
544 }
545 if (ipAddress != null) {
546 IpAddress finalIpAddress = ipAddress;
547 if (hostsList.stream().anyMatch(host -> host.ipAddresses().contains(finalIpAddress)) ||
Seyeon Jeong83e79862020-02-28 01:17:34 -0800548 hostNib.getHostsByIp(finalIpAddress).isEmpty()) {
Andrea Campanella08d07e12018-03-07 14:27:54 -0800549 trace.addResultMessage("Packet is " +
pierventrefe57fda2020-08-04 22:52:02 +0200550 ((EthTypeCriterion) outputPath.getEgressPacket()
Andrea Campanella7fa8f0a2018-03-09 15:30:22 -0800551 .getCriterion(Criterion.Type.ETH_TYPE)).ethType() +
552 " and reached " + cp + " with hosts " + hostsList);
Andrea Campanella08d07e12018-03-07 14:27:54 -0800553 } else {
554 trace.addResultMessage("Wrong output " + cp + " for required destination ip " +
555 ipAddress);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700556 trace.setSuccess(false);
Andrea Campanella08d07e12018-03-07 14:27:54 -0800557 }
558 } else {
pierventrefe57fda2020-08-04 22:52:02 +0200559 trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getEgressPacket()
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800560 .getCriterion(Criterion.Type.ETH_TYPE)).ethType() + " and reached " +
561 cp + " with hosts " + hostsList);
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800562 }
Andrea Campanella2ff88a92018-03-06 15:21:09 -0800563 }
Andrea Campanella08d07e12018-03-07 14:27:54 -0800564 trace.setSuccess(true);
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100565 }
Andrea Campanella8292ba62018-01-31 16:43:23 +0100566 }
Andrea Campanella8292ba62018-01-31 16:43:23 +0100567
568 } else {
pierventrefe57fda2020-08-04 22:52:02 +0200569 TroubleshootUtils.computePath(completePath, trace, cp);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700570 trace.setSuccess(false);
Seyeon Jeong83e79862020-02-28 01:17:34 -0800571 if (deviceNib.getPort(cp) == null) {
Jon Hall4a28fc92018-04-24 18:03:10 -0700572 //Port is not existent on device.
Andrea Campanellae24d4922018-03-26 10:39:22 -0700573 log.warn("Port {} is not available on device.", cp);
574 trace.addResultMessage("Port " + cp + "is not available on device. Packet is dropped");
575 } else {
576 //No links means that the packet gets dropped.
577 log.warn("No links out of {}", cp);
578 trace.addResultMessage("No links depart from " + cp + ". Packet is dropped");
579 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100580 }
581 }
582 return trace;
583 }
584
Andrea Campanellae6798012018-02-06 15:46:52 +0100585 /**
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100586 * Checks if the device has other outputs than the given connect point.
587 *
588 * @param inDeviceId the device
589 * @param trace the trace we are building
590 * @param cp an output connect point
591 * @return true if the device has other outputs.
592 */
593 private boolean hasOtherOutput(DeviceId inDeviceId, StaticPacketTrace trace, ConnectPoint cp) {
pierventrefe57fda2020-08-04 22:52:02 +0200594 return trace.getHitChains(inDeviceId).stream().filter(groupsInDevice ->
595 !groupsInDevice.getOutputPort().equals(cp)).count() > 0;
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100596 }
597
598 /**
Andrea Campanellae4084402017-12-15 15:27:31 +0100599 * Checks if the path contains the device.
600 *
601 * @param completePath the path
602 * @param deviceId the device to check
603 * @return true if the path contains the device
604 */
605 //TODO might prove costly, improvement: a class with both CPs and DeviceIds point.
606 private boolean pathContainsDevice(List<ConnectPoint> completePath, DeviceId deviceId) {
607 for (ConnectPoint cp : completePath) {
608 if (cp.deviceId().equals(deviceId)) {
609 return true;
610 }
611 }
612 return false;
613 }
614
615 /**
616 * Gets the hosts for the given initial packet.
617 *
618 * @param trace the trace we are building
619 * @return set of the hosts we are trying to reach
620 */
621 private Set<Host> getHosts(StaticPacketTrace trace) {
622 IPCriterion ipv4Criterion = ((IPCriterion) trace.getInitialPacket()
623 .getCriterion(Criterion.Type.IPV4_DST));
624 IPCriterion ipv6Criterion = ((IPCriterion) trace.getInitialPacket()
625 .getCriterion(Criterion.Type.IPV6_DST));
626 Set<Host> hosts = new HashSet<>();
627 if (ipv4Criterion != null) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800628 hosts.addAll(hostNib.getHostsByIp(ipv4Criterion.ip().address()));
Andrea Campanellae4084402017-12-15 15:27:31 +0100629 }
630 if (ipv6Criterion != null) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800631 hosts.addAll(hostNib.getHostsByIp(ipv6Criterion.ip().address()));
Andrea Campanellae4084402017-12-15 15:27:31 +0100632 }
633 EthCriterion ethCriterion = ((EthCriterion) trace.getInitialPacket()
634 .getCriterion(Criterion.Type.ETH_DST));
635 if (ethCriterion != null) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800636 hosts.addAll(hostNib.getHostsByMac(ethCriterion.mac()));
Andrea Campanellae4084402017-12-15 15:27:31 +0100637 }
638 return hosts;
639 }
640
641 /**
Andrea Campanellae4084402017-12-15 15:27:31 +0100642 * Traces the packet inside a device starting from an input connect point.
643 *
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700644 * @param trace the trace we are building
645 * @param packet the packet we are tracing
646 * @param in the input connect point.
647 * @param isDualHomed true if the trace we are doing starts or ends in a dual homed host
648 * @param completePath the path up until this device
Andrea Campanellae4084402017-12-15 15:27:31 +0100649 * @return updated trace
650 */
Andrea Campanella696ef032018-02-27 18:03:17 +0100651 private StaticPacketTrace traceInDevice(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in,
Andrea Campanellad5e16ff2018-03-21 10:08:55 -0700652 boolean isDualHomed, List<ConnectPoint> completePath) {
pierventrefe57fda2020-08-04 22:52:02 +0200653 // Get the behavior - do not proceed if there is no PipelineMatchable for the given device
654 PipelineTraceable pipelineMatchable = getPipelineMatchable(in.deviceId());
655 if (pipelineMatchable == null) {
656 trace.addResultMessage("No PipelineMatchable behavior for " + in.deviceId() + ". Aborting");
657 TroubleshootUtils.computePath(completePath, trace, null);
658 trace.setSuccess(false);
659 return trace;
660 }
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100661
pierventrefe57fda2020-08-04 22:52:02 +0200662 // Verify the presence of multiple routes - if the device has been visited in the past
Andrea Campanella08d07e12018-03-07 14:27:54 -0800663 boolean multipleRoutes = false;
pierventrefe57fda2020-08-04 22:52:02 +0200664 if (trace.getHitChains(in.deviceId()) != null) {
Andrea Campanella08d07e12018-03-07 14:27:54 -0800665 multipleRoutes = multipleRoutes(trace);
666 }
pierventrefe57fda2020-08-04 22:52:02 +0200667 if (trace.getHitChains(in.deviceId()) != null && !isDualHomed && !multipleRoutes) {
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100668 log.debug("Trace already contains device and given outputs");
669 return trace;
670 }
Andrea Campanella08d07e12018-03-07 14:27:54 -0800671
Andrea Campanellae4084402017-12-15 15:27:31 +0100672 log.debug("Packet {} coming in from {}", packet, in);
Andrea Campanella17d45192018-01-18 17:11:42 +0100673
674 //if device is not available exit here.
Seyeon Jeong83e79862020-02-28 01:17:34 -0800675 if (!deviceNib.isAvailable(in.deviceId())) {
Andrea Campanella17d45192018-01-18 17:11:42 +0100676 trace.addResultMessage("Device is offline " + in.deviceId());
pierventrefe57fda2020-08-04 22:52:02 +0200677 TroubleshootUtils.computePath(completePath, trace, null);
Andrea Campanella17d45192018-01-18 17:11:42 +0100678 return trace;
679 }
680
pierventrefe57fda2020-08-04 22:52:02 +0200681 // Handle when the input is the controller.
682 // 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 +0100683 // a packet in from the controller will not actually traverse the pipeline and have no such notion
684 // as the input port.
685 if (in.port().equals(PortNumber.CONTROLLER)) {
686 StaticPacketTrace outputTrace = inputFromController(trace, in);
687 if (outputTrace != null) {
688 return trace;
689 }
690 }
691
pierventrefe57fda2020-08-04 22:52:02 +0200692 // Get the device state in the form of DataPlaneEntity objects - do not proceed if there is no state
693 List<DataPlaneEntity> dataPlaneEntities = getDataPlaneEntities(in.deviceId());
694 if (dataPlaneEntities.isEmpty()) {
695 trace.addResultMessage("No device state for " + in.deviceId() + ". Aborting");
696 TroubleshootUtils.computePath(completePath, trace, null);
Andrea Campanella5af2d2b2018-03-12 19:25:44 -0700697 trace.setSuccess(false);
Andrea Campanellae4084402017-12-15 15:27:31 +0100698 return trace;
699 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100700
pierventrefe57fda2020-08-04 22:52:02 +0200701 // Applies pipeline processing
702 PipelineTraceableInput input = new PipelineTraceableInput(packet, in, dataPlaneEntities);
703 PipelineTraceableOutput output = pipelineMatchable.apply(input);
Andrea Campanellae4084402017-12-15 15:27:31 +0100704
pierventrefe57fda2020-08-04 22:52:02 +0200705 // Update the trace
706 List<PipelineTraceableHitChain> hitChains = output.getHitChains();
707 hitChains.forEach(hitChain -> trace.addHitChain(in.deviceId(), hitChain));
708 trace.addResultMessage(output.getLog());
Andrea Campanellae4084402017-12-15 15:27:31 +0100709
pierventrefe57fda2020-08-04 22:52:02 +0200710 // If there was an error set the success to false
711 if (output.getResult() != PipelineTraceableResult.SUCCESS) {
712 TroubleshootUtils.computePath(completePath, trace, null);
713 trace.setSuccess(false);
Andrea Campanellae4084402017-12-15 15:27:31 +0100714 }
715
pierventrefe57fda2020-08-04 22:52:02 +0200716 log.info("Logs -> {}", output.getLog());
717 hitChains.forEach(hitChain -> log.info("HitChain -> {}", hitChain));
Andrea Campanella8292ba62018-01-31 16:43:23 +0100718
pierventrefe57fda2020-08-04 22:52:02 +0200719 // We are done!
Andrea Campanella08d07e12018-03-07 14:27:54 -0800720 return trace;
721 }
722
pierventrefe57fda2020-08-04 22:52:02 +0200723 // Compute whether or not there are multiple routes.
Andrea Campanella08d07e12018-03-07 14:27:54 -0800724 private boolean multipleRoutes(StaticPacketTrace trace) {
725 boolean multipleRoutes = false;
726 IPCriterion ipCriterion = ((IPCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.IPV4_DST));
727 IpAddress ip = null;
728 if (ipCriterion != null) {
729 ip = ipCriterion.ip().address();
730 } else if (trace.getInitialPacket().getCriterion(Criterion.Type.IPV6_DST) != null) {
731 ip = ((IPCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.IPV6_DST)).ip().address();
Andrea Campanella54923d62018-01-23 12:46:04 +0100732 }
Andrea Campanelladd71aca2018-04-04 13:10:45 +0200733 if (ip != null) {
Seyeon Jeong83e79862020-02-28 01:17:34 -0800734 Optional<ResolvedRoute> optionalRoute = routeNib.longestPrefixLookup(ip);
Andrea Campanelladd71aca2018-04-04 13:10:45 +0200735 if (optionalRoute.isPresent()) {
736 ResolvedRoute route = optionalRoute.get();
Seyeon Jeong83e79862020-02-28 01:17:34 -0800737 multipleRoutes = routeNib.getAllResolvedRoutes(route.prefix()).size() > 1;
Andrea Campanelladd71aca2018-04-04 13:10:45 +0200738 }
Andrea Campanella8292ba62018-01-31 16:43:23 +0100739 }
Andrea Campanella08d07e12018-03-07 14:27:54 -0800740 return multipleRoutes;
Andrea Campanellae4084402017-12-15 15:27:31 +0100741 }
742
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100743 /**
744 * Handles the specific case where the Input is the controller.
745 * Note that the in port is used as a convenience to store the port of the controller even if the packet in
746 * from a controller should not have a physical input port. The in port from the Controller is used to make sure
747 * the flood to all active physical ports of the device.
748 *
749 * @param trace the trace
750 * @param in the controller port
751 * @return the augmented trace.
752 */
753 private StaticPacketTrace inputFromController(StaticPacketTrace trace, ConnectPoint in) {
754 EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) trace.getInitialPacket()
755 .getCriterion(Criterion.Type.ETH_TYPE);
756 //If the packet is LLDP or BDDP we flood it on all active ports of the switch.
757 if (ethTypeCriterion != null && (ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
758 || ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
759 //get the active ports
Seyeon Jeong83e79862020-02-28 01:17:34 -0800760 List<Port> enabledPorts = deviceNib.getPorts(in.deviceId()).stream()
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100761 .filter(Port::isEnabled)
762 .collect(Collectors.toList());
763 //build an output from each one
764 enabledPorts.forEach(port -> {
pierventrefe57fda2020-08-04 22:52:02 +0200765 PipelineTraceableHitChain hitChain = new PipelineTraceableHitChain(
766 new ConnectPoint(port.element().id(), port.number()), ImmutableList.of(),
767 trace.getInitialPacket());
768 trace.addHitChain(in.deviceId(), hitChain);
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100769 });
770 return trace;
771 }
772 return null;
773 }
774
pierventrefe57fda2020-08-04 22:52:02 +0200775 ////////////////////////////////
776 // Cemetery - Deprecated code //
777 ////////////////////////////////
778 @Override
779 public List<StaticPacketTrace> pingAll(EtherType type) {
780 ImmutableList.Builder<StaticPacketTrace> tracesBuilder = ImmutableList.builder();
781 hostNib.getHosts().forEach(host -> {
782 List<IpAddress> ipAddresses = getIpAddresses(host, type, false);
783 if (ipAddresses.size() > 0) {
784 //check if the host has only local IPs of that ETH type
785 boolean onlyLocalSrc = ipAddresses.size() == 1 && ipAddresses.get(0).isLinkLocal();
786 hostNib.getHosts().forEach(hostToPing -> {
787 List<IpAddress> ipAddressesToPing = getIpAddresses(hostToPing, type, false);
788 //check if the other host has only local IPs of that ETH type
789 boolean onlyLocalDst = ipAddressesToPing.size() == 1 && ipAddressesToPing.get(0).isLinkLocal();
790 boolean sameLocation = Sets.intersection(host.locations(), hostToPing.locations()).size() > 0;
791 //Trace is done only if they are both local and under the same location
792 // or not local and if they are not the same host.
793 if (((sameLocation && onlyLocalDst && onlyLocalSrc) ||
794 (!onlyLocalSrc && !onlyLocalDst && ipAddressesToPing.size() > 0))
795 && !host.equals(hostToPing)) {
796 tracesBuilder.addAll(trace(host.id(), hostToPing.id(), type));
797 }
798 });
Andrea Campanellae4084402017-12-15 15:27:31 +0100799 }
800 });
pierventrefe57fda2020-08-04 22:52:02 +0200801 return tracesBuilder.build();
Simon Hunt026a2872017-11-13 17:09:43 -0800802 }
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100803
pierventrefe57fda2020-08-04 22:52:02 +0200804 @Override
805 public List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId) {
806 Generator<Set<StaticPacketTrace>> gen = new McastGenerator(mcastRouteNib, this, vlanId);
807 List<Set<StaticPacketTrace>> multicastTraceList =
808 StreamSupport.stream(gen.spliterator(), false).collect(Collectors.toList());
809 return multicastTraceList;
Andrea Campanella4c6170a2018-01-17 16:34:51 +0100810 }
811
Simon Hunt026a2872017-11-13 17:09:43 -0800812}