blob: a1b1d6823ebdd9fb69f7837917624d79e48f5627 [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;
21import com.google.common.collect.Lists;
Simon Hunt026a2872017-11-13 17:09:43 -080022import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Reference;
24import org.apache.felix.scr.annotations.ReferenceCardinality;
25import org.apache.felix.scr.annotations.Service;
Andrea Campanellae4084402017-12-15 15:27:31 +010026import org.onlab.packet.VlanId;
Andrea Campanella54923d62018-01-23 12:46:04 +010027import org.onosproject.cluster.NodeId;
28import org.onosproject.mastership.MastershipService;
Simon Hunt026a2872017-11-13 17:09:43 -080029import org.onosproject.net.ConnectPoint;
Andrea Campanellae4084402017-12-15 15:27:31 +010030import org.onosproject.net.DeviceId;
31import org.onosproject.net.Host;
32import org.onosproject.net.Link;
Andrea Campanella1445f7a2018-02-07 12:00:12 +010033import org.onosproject.net.Port;
Andrea Campanellae4084402017-12-15 15:27:31 +010034import org.onosproject.net.PortNumber;
Andrea Campanella17d45192018-01-18 17:11:42 +010035import org.onosproject.net.device.DeviceService;
Andrea Campanellae4084402017-12-15 15:27:31 +010036import org.onosproject.net.driver.DriverService;
37import org.onosproject.net.flow.DefaultTrafficSelector;
38import org.onosproject.net.flow.FlowEntry;
39import org.onosproject.net.flow.FlowRule;
Simon Hunt026a2872017-11-13 17:09:43 -080040import org.onosproject.net.flow.FlowRuleService;
Andrea Campanellae4084402017-12-15 15:27:31 +010041import org.onosproject.net.flow.IndexTableId;
42import org.onosproject.net.flow.TableId;
Simon Hunt026a2872017-11-13 17:09:43 -080043import org.onosproject.net.flow.TrafficSelector;
Andrea Campanellae4084402017-12-15 15:27:31 +010044import org.onosproject.net.flow.criteria.Criteria;
45import org.onosproject.net.flow.criteria.Criterion;
46import org.onosproject.net.flow.criteria.EthCriterion;
47import org.onosproject.net.flow.criteria.EthTypeCriterion;
48import org.onosproject.net.flow.criteria.IPCriterion;
Andrea Campanellae6798012018-02-06 15:46:52 +010049import org.onosproject.net.flow.criteria.VlanIdCriterion;
Andrea Campanellae4084402017-12-15 15:27:31 +010050import org.onosproject.net.flow.instructions.Instruction;
51import org.onosproject.net.flow.instructions.Instructions;
52import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
53import org.onosproject.net.flow.instructions.L2ModificationInstruction;
54import org.onosproject.net.group.Group;
55import org.onosproject.net.group.GroupBucket;
Simon Hunt026a2872017-11-13 17:09:43 -080056import org.onosproject.net.group.GroupService;
Andrea Campanellae4084402017-12-15 15:27:31 +010057import org.onosproject.net.host.HostService;
58import org.onosproject.net.link.LinkService;
59import org.onosproject.t3.api.GroupsInDevice;
Simon Hunt026a2872017-11-13 17:09:43 -080060import org.onosproject.t3.api.StaticPacketTrace;
61import org.onosproject.t3.api.TroubleshootService;
62import org.slf4j.Logger;
63
Andrea Campanellae4084402017-12-15 15:27:31 +010064import java.net.UnknownHostException;
65import java.util.ArrayList;
Andrea Campanella54923d62018-01-23 12:46:04 +010066import java.util.Collection;
Andrea Campanellae4084402017-12-15 15:27:31 +010067import java.util.Collections;
68import java.util.Comparator;
69import java.util.HashSet;
70import java.util.List;
71import java.util.Set;
72import java.util.stream.Collectors;
73
74import static org.onlab.packet.EthType.EtherType;
Andrea Campanellae6798012018-02-06 15:46:52 +010075import static org.onosproject.net.flow.TrafficSelector.Builder;
Andrea Campanellae4084402017-12-15 15:27:31 +010076import static org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
Andrea Campanella94c594a2018-02-06 18:58:40 +010077import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
78import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsHeaderInstruction;
79import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
80import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Simon Hunt026a2872017-11-13 17:09:43 -080081import static org.slf4j.LoggerFactory.getLogger;
82
83/**
Andrea Campanellae4084402017-12-15 15:27:31 +010084 * Manager to troubleshoot packets inside the network.
85 * Given a representation of a packet follows it's path in the network according to the existing flows and groups in
86 * the devices.
Simon Hunt026a2872017-11-13 17:09:43 -080087 */
88@Service
89@Component(immediate = true)
90public class TroubleshootManager implements TroubleshootService {
91
92 private static final Logger log = getLogger(TroubleshootManager.class);
93
Andrea Campanella1445f7a2018-02-07 12:00:12 +010094 static final String PACKET_TO_CONTROLLER = "Packet goes to the controller";
95
Simon Hunt026a2872017-11-13 17:09:43 -080096 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected FlowRuleService flowRuleService;
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected GroupService groupService;
101
Andrea Campanellae4084402017-12-15 15:27:31 +0100102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected LinkService linkService;
Simon Hunt026a2872017-11-13 17:09:43 -0800104
Andrea Campanellae4084402017-12-15 15:27:31 +0100105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected HostService hostService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DriverService driverService;
Simon Hunt026a2872017-11-13 17:09:43 -0800110
Andrea Campanella17d45192018-01-18 17:11:42 +0100111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected DeviceService deviceService;
113
Andrea Campanella54923d62018-01-23 12:46:04 +0100114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected MastershipService mastershipService;
116
Simon Hunt026a2872017-11-13 17:09:43 -0800117 @Override
118 public StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in) {
Andrea Campanellae4084402017-12-15 15:27:31 +0100119 log.info("Tracing packet {} coming in through {}", packet, in);
Andrea Campanella17d45192018-01-18 17:11:42 +0100120 //device must exist in ONOS
121 Preconditions.checkNotNull(deviceService.getDevice(in.deviceId()),
122 "Device " + in.deviceId() + " must exist in ONOS");
123
Andrea Campanellae4084402017-12-15 15:27:31 +0100124 StaticPacketTrace trace = new StaticPacketTrace(packet, in);
125 //FIXME this can be done recursively
126 trace = traceInDevice(trace, packet, in);
127 //Building output connect Points
128 List<ConnectPoint> path = new ArrayList<>();
129 trace = getTrace(path, in, trace);
130 return trace;
131 }
132
133 /**
134 * Computes a trace for a give packet that start in the network at the given connect point.
135 *
136 * @param completePath the path traversed by the packet
137 * @param in the input connect point
138 * @param trace the trace to build
139 * @return the build trace for that packet.
140 */
141 private StaticPacketTrace getTrace(List<ConnectPoint> completePath, ConnectPoint in, StaticPacketTrace trace) {
142
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100143 log.debug("------------------------------------------------------------");
144
Andrea Campanellae4084402017-12-15 15:27:31 +0100145 //if the trace already contains the input connect point there is a loop
146 if (pathContainsDevice(completePath, in.deviceId())) {
147 trace.addResultMessage("Loop encountered in device " + in.deviceId());
148 return trace;
149 }
150
151 //let's add the input connect point
152 completePath.add(in);
153
154 //If the trace has no outputs for the given input we stop here
155 if (trace.getGroupOuputs(in.deviceId()) == null) {
156 computePath(completePath, trace, null);
157 trace.addResultMessage("No output out of device " + in.deviceId() + ". Packet is dropped");
158 return trace;
159 }
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100160
Andrea Campanellae4084402017-12-15 15:27:31 +0100161 //If the trace has ouputs we analyze them all
162 for (GroupsInDevice outputPath : trace.getGroupOuputs(in.deviceId())) {
Andrea Campanella54923d62018-01-23 12:46:04 +0100163
164 ConnectPoint cp = outputPath.getOutput();
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100165 log.debug("Connect point in {}", in);
Andrea Campanella54923d62018-01-23 12:46:04 +0100166 log.debug("Output path {}", cp);
167
Andrea Campanellae4084402017-12-15 15:27:31 +0100168 //Hosts for the the given output
Andrea Campanella54923d62018-01-23 12:46:04 +0100169 Set<Host> hostsList = hostService.getConnectedHosts(cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100170 //Hosts queried from the original ip or mac
171 Set<Host> hosts = getHosts(trace);
172
173 //If the two host collections contain the same item it means we reached the proper output
174 if (!Collections.disjoint(hostsList, hosts)) {
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100175 log.debug("Stopping here because host is expected destination {}, reached through", completePath);
Andrea Campanella54923d62018-01-23 12:46:04 +0100176 trace.addResultMessage("Reached required destination Host " + cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100177 computePath(completePath, trace, outputPath.getOutput());
178 break;
Andrea Campanella54923d62018-01-23 12:46:04 +0100179 } else if (cp.port().equals(PortNumber.CONTROLLER)) {
Andrea Campanella8292ba62018-01-31 16:43:23 +0100180
Andrea Campanella54923d62018-01-23 12:46:04 +0100181 //Getting the master when the packet gets sent as packet in
182 NodeId master = mastershipService.getMasterFor(cp.deviceId());
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100183 trace.addResultMessage(PACKET_TO_CONTROLLER + " " + master.id());
Andrea Campanella54923d62018-01-23 12:46:04 +0100184 computePath(completePath, trace, outputPath.getOutput());
Andrea Campanellae6798012018-02-06 15:46:52 +0100185 handleVlanToController(outputPath, trace);
Andrea Campanella54923d62018-01-23 12:46:04 +0100186
Andrea Campanella8292ba62018-01-31 16:43:23 +0100187 } else if (linkService.getEgressLinks(cp).size() > 0) {
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100188
189 //TODO this can be optimized if we use a Tree structure for paths.
190 //if we already have outputs let's check if the one we are considering starts from one of the devices
191 // in any of the ones we have.
192 if (trace.getCompletePaths().size() > 0) {
193 ConnectPoint inputForOutput = null;
194 List<ConnectPoint> previousPath = new ArrayList<>();
195 for (List<ConnectPoint> path : trace.getCompletePaths()) {
196 for (ConnectPoint connect : path) {
197 //if the path already contains the input for the output we've found we use it
198 if (connect.equals(in)) {
199 inputForOutput = connect;
200 previousPath = path;
201 break;
202 }
203 }
204 }
205 //we use the pre-existing path up to the point we fork to a new output
206 if (inputForOutput != null && completePath.contains(inputForOutput)) {
207 List<ConnectPoint> temp = new ArrayList<>(previousPath);
208 completePath = temp.subList(0, previousPath.indexOf(inputForOutput) + 1);
209 }
210 }
211
Andrea Campanellae4084402017-12-15 15:27:31 +0100212 //let's add the ouput for the input
213 completePath.add(cp);
Andrea Campanellae4084402017-12-15 15:27:31 +0100214 //let's compute the links for the given output
215 Set<Link> links = linkService.getEgressLinks(cp);
216 log.debug("Egress Links {}", links);
Andrea Campanellae4084402017-12-15 15:27:31 +0100217 //For each link we trace the corresponding device
218 for (Link link : links) {
219 ConnectPoint dst = link.dst();
220 //change in-port to the dst link in port
Andrea Campanella8292ba62018-01-31 16:43:23 +0100221 Builder updatedPacket = DefaultTrafficSelector.builder();
Andrea Campanellae4084402017-12-15 15:27:31 +0100222 outputPath.getFinalPacket().criteria().forEach(updatedPacket::add);
223 updatedPacket.add(Criteria.matchInPort(dst.port()));
224 log.debug("DST Connect Point {}", dst);
225 //build the elements for that device
226 traceInDevice(trace, updatedPacket.build(), dst);
227 //continue the trace along the path
228 getTrace(completePath, dst, trace);
229 }
230
Andrea Campanella8292ba62018-01-31 16:43:23 +0100231 } else if (deviceService.getPort(cp).isEnabled()) {
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100232 EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) trace.getInitialPacket()
233 .getCriterion(Criterion.Type.ETH_TYPE);
234 //We treat as correct output only if it's not LLDP or BDDP
235 if (!(ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
236 || !ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
237 if (hostsList.isEmpty()) {
238 trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getFinalPacket()
239 .getCriterion(Criterion.Type.ETH_TYPE)).ethType() + " and reached " +
240 cp + " with no hosts connected ");
241 } else {
242 trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getFinalPacket()
243 .getCriterion(Criterion.Type.ETH_TYPE)).ethType() + " and reached " +
244 cp + " with hosts " + hostsList);
245 }
246 computePath(completePath, trace, outputPath.getOutput());
Andrea Campanella8292ba62018-01-31 16:43:23 +0100247 }
Andrea Campanella8292ba62018-01-31 16:43:23 +0100248
249 } else {
250 //No links means that the packet gets dropped.
251 log.warn("No links out of {}", cp);
252 computePath(completePath, trace, cp);
253 trace.addResultMessage("No links depart from " + cp + ". Packet is dropped");
Andrea Campanellae4084402017-12-15 15:27:31 +0100254 }
255 }
256 return trace;
257 }
258
259 /**
Andrea Campanellae6798012018-02-06 15:46:52 +0100260 * If the initial packet comes tagged with a Vlan we output it with that to ONOS.
261 * If ONOS applied a vlan we remove it.
262 *
263 * @param outputPath the output
264 * @param trace the trace we are building
265 */
266 private void handleVlanToController(GroupsInDevice outputPath, StaticPacketTrace trace) {
267
268 VlanIdCriterion initialVid = (VlanIdCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.VLAN_VID);
269 VlanIdCriterion finalVid = (VlanIdCriterion) outputPath.getFinalPacket().getCriterion(Criterion.Type.VLAN_VID);
270
271 if (initialVid != null && !initialVid.equals(finalVid) && initialVid.vlanId().equals(VlanId.NONE)) {
272
273 Set<Criterion> finalCriteria = new HashSet<>(outputPath.getFinalPacket().criteria());
274 //removing the final vlanId
275 finalCriteria.remove(finalVid);
276 Builder packetUpdated = DefaultTrafficSelector.builder();
277 finalCriteria.forEach(packetUpdated::add);
278 //Initial was none so we set it to that
279 packetUpdated.add(Criteria.matchVlanId(VlanId.NONE));
280 //Update final packet
281 outputPath.setFinalPacket(packetUpdated.build());
282 }
283 }
284
285 /**
Andrea Campanellae4084402017-12-15 15:27:31 +0100286 * Checks if the path contains the device.
287 *
288 * @param completePath the path
289 * @param deviceId the device to check
290 * @return true if the path contains the device
291 */
292 //TODO might prove costly, improvement: a class with both CPs and DeviceIds point.
293 private boolean pathContainsDevice(List<ConnectPoint> completePath, DeviceId deviceId) {
294 for (ConnectPoint cp : completePath) {
295 if (cp.deviceId().equals(deviceId)) {
296 return true;
297 }
298 }
299 return false;
300 }
301
302 /**
303 * Gets the hosts for the given initial packet.
304 *
305 * @param trace the trace we are building
306 * @return set of the hosts we are trying to reach
307 */
308 private Set<Host> getHosts(StaticPacketTrace trace) {
309 IPCriterion ipv4Criterion = ((IPCriterion) trace.getInitialPacket()
310 .getCriterion(Criterion.Type.IPV4_DST));
311 IPCriterion ipv6Criterion = ((IPCriterion) trace.getInitialPacket()
312 .getCriterion(Criterion.Type.IPV6_DST));
313 Set<Host> hosts = new HashSet<>();
314 if (ipv4Criterion != null) {
315 hosts.addAll(hostService.getHostsByIp(ipv4Criterion.ip().address()));
316 }
317 if (ipv6Criterion != null) {
318 hosts.addAll(hostService.getHostsByIp(ipv6Criterion.ip().address()));
319 }
320 EthCriterion ethCriterion = ((EthCriterion) trace.getInitialPacket()
321 .getCriterion(Criterion.Type.ETH_DST));
322 if (ethCriterion != null) {
323 hosts.addAll(hostService.getHostsByMac(ethCriterion.mac()));
324 }
325 return hosts;
326 }
327
328 /**
329 * Computes the list of traversed connect points.
330 *
331 * @param completePath the list of devices
332 * @param trace the trace we are building
333 * @param output the final output connect point
334 */
335 private void computePath(List<ConnectPoint> completePath, StaticPacketTrace trace, ConnectPoint output) {
336 List<ConnectPoint> traverseList = new ArrayList<>();
337 if (!completePath.contains(trace.getInitialConnectPoint())) {
338 traverseList.add(trace.getInitialConnectPoint());
339 }
340 traverseList.addAll(completePath);
341 if (output != null && !completePath.contains(output)) {
342 traverseList.add(output);
343 }
344 trace.addCompletePath(traverseList);
Andrea Campanellae4084402017-12-15 15:27:31 +0100345 }
346
347 /**
348 * Traces the packet inside a device starting from an input connect point.
349 *
350 * @param trace the trace we are building
351 * @param packet the packet we are tracing
352 * @param in the input connect point.
353 * @return updated trace
354 */
355 private StaticPacketTrace traceInDevice(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in) {
Andrea Campanellab022b5e2018-01-31 14:59:03 +0100356
357 //we already traversed this device.
358 if (trace.getGroupOuputs(in.deviceId()) != null) {
359 log.debug("Trace already contains device and given outputs");
360 return trace;
361 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100362 log.debug("Packet {} coming in from {}", packet, in);
Andrea Campanella17d45192018-01-18 17:11:42 +0100363
364 //if device is not available exit here.
365 if (!deviceService.isAvailable(in.deviceId())) {
366 trace.addResultMessage("Device is offline " + in.deviceId());
367 return trace;
368 }
369
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100370 //handle when the input is the controller
371 //NOTE, we are using the input port as a convenience to carry the CONTROLLER port number even if
372 // a packet in from the controller will not actually traverse the pipeline and have no such notion
373 // as the input port.
374 if (in.port().equals(PortNumber.CONTROLLER)) {
375 StaticPacketTrace outputTrace = inputFromController(trace, in);
376 if (outputTrace != null) {
377 return trace;
378 }
379 }
380
Andrea Campanellae4084402017-12-15 15:27:31 +0100381 List<FlowEntry> flows = new ArrayList<>();
382 List<FlowEntry> outputFlows = new ArrayList<>();
383
Andrea Campanella8292ba62018-01-31 16:43:23 +0100384 List<Instruction> deferredInstructions = new ArrayList<>();
385
Andrea Campanellae4084402017-12-15 15:27:31 +0100386 FlowEntry nextTableIdEntry = findNextTableIdEntry(in.deviceId(), -1);
387 if (nextTableIdEntry == null) {
388 trace.addResultMessage("No flow rules for device " + in.deviceId() + ". Aborting");
389 return trace;
390 }
391 TableId tableId = nextTableIdEntry.table();
392 FlowEntry flowEntry;
393 boolean output = false;
394 while (!output) {
395 log.debug("Searching a Flow Entry on table {} for packet {}", tableId, packet);
396 //get the rule that matches the incoming packet
397 flowEntry = matchHighestPriority(packet, in, tableId);
398 log.debug("Found Flow Entry {}", flowEntry);
399
400 boolean isOfdpaHardware = TroubleshootUtils.hardwareOfdpaMap
401 .getOrDefault(driverService.getDriver(in.deviceId()).name(), false);
402
403 //if the flow entry on a table is null and we are on hardware we treat as table miss, with few exceptions
404 if (flowEntry == null && isOfdpaHardware) {
405 log.debug("Ofdpa Hw setup, no flow rule means table miss");
406
Andrea Campanellae4084402017-12-15 15:27:31 +0100407 if (((IndexTableId) tableId).id() == 27) {
408 //Apparently a miss but Table 27 on OFDPA is a fixed table
409 packet = handleOfdpa27FixedTable(trace, packet);
410 }
411
412 //Finding next table to go In case of miss
413 nextTableIdEntry = findNextTableIdEntry(in.deviceId(), ((IndexTableId) tableId).id());
414 log.debug("Next table id entry {}", nextTableIdEntry);
415
416 //FIXME find better solution that enable granularity greater than 0 or all rules
417 //(another possibility is max tableId)
418 if (nextTableIdEntry == null && flows.size() == 0) {
Andrea Campanella09eec852018-02-05 19:39:25 +0100419 trace.addResultMessage("No matching flow rules for device " + in.deviceId() + ". Aborting");
Andrea Campanellae4084402017-12-15 15:27:31 +0100420 return trace;
421
422 } else if (nextTableIdEntry == null) {
423 //Means that no more flow rules are present
424 output = true;
425
426 } else if (((IndexTableId) tableId).id() == 20) {
427 //if the table is 20 OFDPA skips to table 50
428 log.debug("A miss on Table 20 on OFDPA means that we skip directly to table 50");
429 tableId = IndexTableId.of(50);
430
431 } else {
432 tableId = nextTableIdEntry.table();
433 }
434
Andrea Campanellae4084402017-12-15 15:27:31 +0100435 } else if (flowEntry == null) {
436 trace.addResultMessage("Packet has no match on table " + tableId + " in device " +
437 in.deviceId() + ". Dropping");
438 return trace;
439 } else {
Andrea Campanella94c594a2018-02-06 18:58:40 +0100440
Andrea Campanellae4084402017-12-15 15:27:31 +0100441 //IF the table has a transition
442 if (flowEntry.treatment().tableTransition() != null) {
443 //update the next table we transitions to
444 tableId = IndexTableId.of(flowEntry.treatment().tableTransition().tableId());
445 log.debug("Flow Entry has transition to table Id {}", tableId);
446 flows.add(flowEntry);
447 } else {
448 //table has no transition so it means that it's an output rule if on the last table
449 log.debug("Flow Entry has no transition to table, treating as last rule {}", flowEntry);
450 flows.add(flowEntry);
451 outputFlows.add(flowEntry);
452 output = true;
453 }
Andrea Campanella8292ba62018-01-31 16:43:23 +0100454 //update the packet according to the immediate actions of this flow rule.
455 packet = updatePacket(packet, flowEntry.treatment().immediate()).build();
456
457 //save the deferred rules for later
458 deferredInstructions.addAll(flowEntry.treatment().deferred());
459
460 //If the flow requires to clear deferred actions we do so for all the ones we encountered.
461 if (flowEntry.treatment().clearedDeferred()) {
462 deferredInstructions.clear();
463 }
464
Andrea Campanella94c594a2018-02-06 18:58:40 +0100465 //On table 10 OFDPA needs two rules to apply the vlan if none and then to transition to the next table.
466 if (needsSecondTable10Flow(flowEntry, isOfdpaHardware)) {
467
468 //Let's get the packet vlanId instruction
469 VlanIdCriterion packetVlanIdCriterion =
470 (VlanIdCriterion) packet.getCriterion(Criterion.Type.VLAN_VID);
471
472 //Let's get the flow entry vlan mod instructions
473 ModVlanIdInstruction entryModVlanIdInstruction = (ModVlanIdInstruction) flowEntry.treatment()
474 .immediate().stream()
475 .filter(instruction -> instruction instanceof ModVlanIdInstruction)
476 .findFirst().orElse(null);
477
478 //If the entry modVlan is not null we need to make sure that the packet has been updated and there
479 // is a flow rule that matches on same criteria and with updated vlanId
480 if (entryModVlanIdInstruction != null) {
481
482 FlowEntry secondVlanFlow = getSecondFlowEntryOnTable10(packet, in,
483 packetVlanIdCriterion, entryModVlanIdInstruction);
484
485 //We found the flow that we expected
486 if (secondVlanFlow != null) {
487 flows.add(secondVlanFlow);
488 } else {
489 trace.addResultMessage("Missing forwarding rule for tagged packet on " + in);
490 return trace;
491 }
492 }
493
494 }
495
Andrea Campanellae4084402017-12-15 15:27:31 +0100496 }
497 }
498
499 //Creating a modifiable builder for the output packet
Andrea Campanella8292ba62018-01-31 16:43:23 +0100500 Builder builder = DefaultTrafficSelector.builder();
Andrea Campanellae4084402017-12-15 15:27:31 +0100501 packet.criteria().forEach(builder::add);
Andrea Campanella8292ba62018-01-31 16:43:23 +0100502
Andrea Campanellae4084402017-12-15 15:27:31 +0100503 //Adding all the flows to the trace
Andrea Campanella54923d62018-01-23 12:46:04 +0100504 trace.addFlowsForDevice(in.deviceId(), ImmutableList.copyOf(flows));
Andrea Campanellae4084402017-12-15 15:27:31 +0100505
Andrea Campanellae4084402017-12-15 15:27:31 +0100506 List<PortNumber> outputPorts = new ArrayList<>();
507
Andrea Campanella54923d62018-01-23 12:46:04 +0100508 //TODO optimization
Andrea Campanella54923d62018-01-23 12:46:04 +0100509 //outputFlows contains also last rule of device, so we need filtering for OUTPUT instructions.
510 List<FlowEntry> outputFlowEntries = outputFlows.stream().filter(flow -> flow.treatment()
511 .allInstructions().stream().filter(instruction -> instruction.type()
512 .equals(Instruction.Type.OUTPUT)).count() > 0).collect(Collectors.toList());
Andrea Campanellae4084402017-12-15 15:27:31 +0100513
Andrea Campanella54923d62018-01-23 12:46:04 +0100514 if (outputFlowEntries.size() > 1) {
515 trace.addResultMessage("More than one flow rule with OUTPUT instruction");
516 log.warn("There cannot be more than one flow entry with OUTPUT instruction for {}", packet);
Andrea Campanellae4084402017-12-15 15:27:31 +0100517 }
Andrea Campanella54923d62018-01-23 12:46:04 +0100518
519 if (outputFlowEntries.size() == 1) {
520
521 OutputInstruction outputInstruction = (OutputInstruction) outputFlowEntries.get(0).treatment()
522 .allInstructions().stream()
523 .filter(instruction -> {
524 return instruction.type().equals(Instruction.Type.OUTPUT);
525 }).findFirst().get();
526
527 //FIXME using GroupsInDevice for output even if flows.
528 buildOutputFromDevice(trace, in, builder, outputPorts, outputInstruction, ImmutableList.of());
529
530 }
531 log.debug("Handling Groups");
532 //Analyze Groups
533 List<Group> groups = new ArrayList<>();
534
535 Collection<FlowEntry> nonOutputFlows = flows;
536 nonOutputFlows.removeAll(outputFlowEntries);
537
Andrea Campanella8292ba62018-01-31 16:43:23 +0100538 //Handling groups pointed at by immediate instructions
Andrea Campanella54923d62018-01-23 12:46:04 +0100539 for (FlowEntry entry : flows) {
Andrea Campanella8292ba62018-01-31 16:43:23 +0100540 getGroupsFromInstructions(trace, groups, entry.treatment().immediate(),
Andrea Campanella54923d62018-01-23 12:46:04 +0100541 entry.deviceId(), builder, outputPorts, in);
542 }
Andrea Campanella8292ba62018-01-31 16:43:23 +0100543
544 //If we have deferred instructions at this point we handle them.
545 if (deferredInstructions.size() > 0) {
546 builder = handleDeferredActions(trace, packet, in, deferredInstructions, outputPorts, groups);
547
548 }
Andrea Campanella54923d62018-01-23 12:46:04 +0100549 packet = builder.build();
Andrea Campanella54923d62018-01-23 12:46:04 +0100550
Andrea Campanella94c594a2018-02-06 18:58:40 +0100551 log.debug("Output Packet {}", packet);
Andrea Campanellae4084402017-12-15 15:27:31 +0100552 return trace;
553 }
554
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100555 /**
556 * Handles the specific case where the Input is the controller.
557 * Note that the in port is used as a convenience to store the port of the controller even if the packet in
558 * from a controller should not have a physical input port. The in port from the Controller is used to make sure
559 * the flood to all active physical ports of the device.
560 *
561 * @param trace the trace
562 * @param in the controller port
563 * @return the augmented trace.
564 */
565 private StaticPacketTrace inputFromController(StaticPacketTrace trace, ConnectPoint in) {
566 EthTypeCriterion ethTypeCriterion = (EthTypeCriterion) trace.getInitialPacket()
567 .getCriterion(Criterion.Type.ETH_TYPE);
568 //If the packet is LLDP or BDDP we flood it on all active ports of the switch.
569 if (ethTypeCriterion != null && (ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
570 || ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
571 //get the active ports
572 List<Port> enabledPorts = deviceService.getPorts(in.deviceId()).stream()
573 .filter(Port::isEnabled)
574 .collect(Collectors.toList());
575 //build an output from each one
576 enabledPorts.forEach(port -> {
577 GroupsInDevice output = new GroupsInDevice(new ConnectPoint(port.element().id(), port.number()),
578 ImmutableList.of(), trace.getInitialPacket());
579 trace.addGroupOutputPath(in.deviceId(), output);
580 });
581 return trace;
582 }
583 return null;
584 }
585
Andrea Campanella94c594a2018-02-06 18:58:40 +0100586 private boolean needsSecondTable10Flow(FlowEntry flowEntry, boolean isOfdpaHardware) {
587 return isOfdpaHardware && flowEntry.table().equals(IndexTableId.of(10))
588 && flowEntry.selector().getCriterion(Criterion.Type.VLAN_VID) != null
589 && ((VlanIdCriterion) flowEntry.selector().getCriterion(Criterion.Type.VLAN_VID))
590 .vlanId().equals(VlanId.NONE);
591 }
592
593 /**
594 * Method that finds a flow rule on table 10 that matches the packet and the VLAN of the already
595 * found rule on table 10. This is because OFDPA needs two rules on table 10, first to apply the rule,
596 * second to transition to following table
597 *
598 * @param packet the incoming packet
599 * @param in the input connect point
600 * @param packetVlanIdCriterion the vlan criterion from the packet
601 * @param entryModVlanIdInstruction the entry vlan instruction
602 * @return the second flow entry that matched
603 */
604 private FlowEntry getSecondFlowEntryOnTable10(TrafficSelector packet, ConnectPoint in,
605 VlanIdCriterion packetVlanIdCriterion,
606 ModVlanIdInstruction entryModVlanIdInstruction) {
607 FlowEntry secondVlanFlow = null;
608 //Check the packet has been update from the first rule.
609 if (packetVlanIdCriterion.vlanId().equals(entryModVlanIdInstruction.vlanId())) {
610 //find a rule on the same table that matches the vlan and
611 // also all the other elements of the flow such as input port
612 secondVlanFlow = Lists.newArrayList(flowRuleService.getFlowEntries(in.deviceId()).iterator())
613 .stream()
614 .filter(entry -> {
615 return entry.table().equals(IndexTableId.of(10));
616 })
617 .filter(entry -> {
618 VlanIdCriterion criterion = (VlanIdCriterion) entry.selector()
619 .getCriterion(Criterion.Type.VLAN_VID);
620 return criterion != null && match(packet, entry)
621 && criterion.vlanId().equals(entryModVlanIdInstruction.vlanId());
622 }).findFirst().orElse(null);
623
624 }
625 return secondVlanFlow;
626 }
627
Andrea Campanella8292ba62018-01-31 16:43:23 +0100628
Andrea Campanellae4084402017-12-15 15:27:31 +0100629 /**
Andrea Campanellae4084402017-12-15 15:27:31 +0100630 * Handles table 27 in Ofpda which is a fixed table not visible to any controller that handles Mpls Labels.
631 *
632 * @param packet the incoming packet
633 * @return the updated packet
634 */
635 private TrafficSelector handleOfdpa27FixedTable(StaticPacketTrace trace, TrafficSelector packet) {
636 log.debug("Handling table 27 on OFDPA, removing mpls ETH Type and change mpls label");
637 Criterion mplsCriterion = packet.getCriterion(Criterion.Type.ETH_TYPE);
638 ImmutableList.Builder<Instruction> builder = ImmutableList.builder();
639
640 //If the pakcet comes in with the expected elements we update it as per OFDPA spec.
641 if (mplsCriterion != null && ((EthTypeCriterion) mplsCriterion).ethType()
642 .equals(EtherType.MPLS_UNICAST.ethType())) {
Andrea Campanella3970e472018-01-25 16:44:04 +0100643 //TODO update with parsing with eth MPLS pop Instruction for treating label an bos
Andrea Campanellae4084402017-12-15 15:27:31 +0100644 Instruction ethInstruction = Instructions.popMpls(((EthTypeCriterion) trace.getInitialPacket()
645 .getCriterion(Criterion.Type.ETH_TYPE)).ethType());
646 //FIXME what do we use as L3_Unicast mpls Label ?
Andrea Campanella3970e472018-01-25 16:44:04 +0100647 //translateInstruction(builder, ethInstruction);
Andrea Campanellae4084402017-12-15 15:27:31 +0100648 builder.add(ethInstruction);
649 }
650 packet = updatePacket(packet, builder.build()).build();
651 return packet;
652 }
653
654 /**
655 * Finds the flow entry with the minimun next table Id.
656 *
657 * @param deviceId the device to search
658 * @param currentId the current id. the search will use this as minimum
659 * @return the flow entry with the minimum table Id after the given one.
660 */
661 private FlowEntry findNextTableIdEntry(DeviceId deviceId, int currentId) {
662
663 final Comparator<FlowEntry> comparator = Comparator.comparing((FlowEntry f) -> ((IndexTableId) f.table()).id());
664
665 return Lists.newArrayList(flowRuleService.getFlowEntries(deviceId).iterator())
666 .stream().filter(f -> ((IndexTableId) f.table()).id() > currentId).min(comparator).orElse(null);
667 }
668
Andrea Campanella8292ba62018-01-31 16:43:23 +0100669 private Builder handleDeferredActions(StaticPacketTrace trace, TrafficSelector packet,
670 ConnectPoint in, List<Instruction> deferredInstructions,
671 List<PortNumber> outputPorts, List<Group> groups) {
672
673 //Update the packet with the deferred instructions
674 Builder builder = updatePacket(packet, deferredInstructions);
675
676 //Gather any output instructions from the deferred instruction
677 List<Instruction> outputFlowInstruction = deferredInstructions.stream().filter(instruction -> {
678 return instruction.type().equals(Instruction.Type.OUTPUT);
679 }).collect(Collectors.toList());
680
681 //We are considering deferred instructions from flows, there can only be one output.
682 if (outputFlowInstruction.size() > 1) {
683 trace.addResultMessage("More than one flow rule with OUTPUT instruction");
684 log.warn("There cannot be more than one flow entry with OUTPUT instruction for {}", packet);
685 }
686 //If there is one output let's go through that
687 if (outputFlowInstruction.size() == 1) {
688 buildOutputFromDevice(trace, in, builder, outputPorts, (OutputInstruction) outputFlowInstruction.get(0),
689 ImmutableList.of());
690 }
691 //If there is no output let's see if there any deferred instruction point to groups.
692 if (outputFlowInstruction.size() == 0) {
693 getGroupsFromInstructions(trace, groups, deferredInstructions,
694 in.deviceId(), builder, outputPorts, in);
695 }
696 return builder;
697 }
698
Andrea Campanellae4084402017-12-15 15:27:31 +0100699 /**
700 * Gets group information from instructions.
701 *
702 * @param trace the trace we are building
703 * @param groupsForDevice the set of groups for this device
704 * @param instructions the set of instructions we are searching for groups.
705 * @param deviceId the device we are considering
706 * @param builder the builder of the input packet
707 * @param outputPorts the output ports for that packet
708 */
709 private void getGroupsFromInstructions(StaticPacketTrace trace, List<Group> groupsForDevice,
710 List<Instruction> instructions, DeviceId deviceId,
Andrea Campanella8292ba62018-01-31 16:43:23 +0100711 Builder builder, List<PortNumber> outputPorts,
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100712 ConnectPoint in) {
Andrea Campanellae4084402017-12-15 15:27:31 +0100713 List<Instruction> groupInstructionlist = new ArrayList<>();
714 for (Instruction instruction : instructions) {
715 log.debug("Considering Instruction {}", instruction);
716 //if the instruction is not group we need to update the packet or add the output
717 //to the possible outputs for this packet
718 if (!instruction.type().equals(Instruction.Type.GROUP)) {
719 //if the instruction is not group we need to update the packet or add the output
720 //to the possible outputs for this packet
721 if (instruction.type().equals(Instruction.Type.OUTPUT)) {
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100722 buildOutputFromDevice(trace, in, builder, outputPorts,
723 (OutputInstruction) instruction, groupsForDevice);
Andrea Campanellae4084402017-12-15 15:27:31 +0100724 } else {
725 builder = translateInstruction(builder, instruction);
726 }
727 } else {
728 //if the instuction is pointing to a group we need to get the group
729 groupInstructionlist.add(instruction);
730 }
731 }
732 //handle all the internal instructions pointing to a group.
733 for (Instruction instr : groupInstructionlist) {
734 GroupInstruction groupInstruction = (GroupInstruction) instr;
735 Group group = Lists.newArrayList(groupService.getGroups(deviceId)).stream().filter(groupInternal -> {
736 return groupInternal.id().equals(groupInstruction.groupId());
737 }).findAny().orElse(null);
738 if (group == null) {
739 trace.addResultMessage("Null group for Instruction " + instr);
740 break;
741 }
742 //add the group to the traversed groups
743 groupsForDevice.add(group);
744 //Cycle in each of the group's buckets and add them to the groups for this Device.
745 for (GroupBucket bucket : group.buckets().buckets()) {
746 getGroupsFromInstructions(trace, groupsForDevice, bucket.treatment().allInstructions(),
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100747 deviceId, builder, outputPorts, in);
Andrea Campanellae4084402017-12-15 15:27:31 +0100748 }
749 }
750 }
751
752 /**
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100753 * Check if the output is the input port, if so adds a dop result message, otherwise builds
754 * a possible output from this device.
755 *
756 * @param trace the trace
757 * @param in the input connect point
758 * @param builder the packet builder
759 * @param outputPorts the list of output ports for this device
760 * @param outputInstruction the output instruction
Andrea Campanella8292ba62018-01-31 16:43:23 +0100761 * @param groupsForDevice the groups we output from
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100762 */
Andrea Campanella8292ba62018-01-31 16:43:23 +0100763 private void buildOutputFromDevice(StaticPacketTrace trace, ConnectPoint in, Builder builder,
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100764 List<PortNumber> outputPorts, OutputInstruction outputInstruction,
765 List<Group> groupsForDevice) {
Andrea Campanella54923d62018-01-23 12:46:04 +0100766 ConnectPoint output = new ConnectPoint(in.deviceId(), outputInstruction.port());
Andrea Campanella1445f7a2018-02-07 12:00:12 +0100767
768 //if the output is the input same we drop, except if the output is the controller
Andrea Campanella7d3cf652018-01-22 15:10:30 +0100769 if (output.equals(in)) {
770 trace.addResultMessage("Connect point out " + output + " is same as initial input " +
771 trace.getInitialConnectPoint());
772 } else {
773 trace.addGroupOutputPath(in.deviceId(),
774 new GroupsInDevice(output, groupsForDevice, builder.build()));
775 outputPorts.add(outputInstruction.port());
776 }
777 }
778
779 /**
Andrea Campanellae4084402017-12-15 15:27:31 +0100780 * Applies all give instructions to the input packet.
781 *
782 * @param packet the input packet
783 * @param instructions the set of instructions
784 * @return the packet with the applied instructions
785 */
Andrea Campanella8292ba62018-01-31 16:43:23 +0100786 private Builder updatePacket(TrafficSelector packet, List<Instruction> instructions) {
787 Builder newSelector = DefaultTrafficSelector.builder();
Andrea Campanellae4084402017-12-15 15:27:31 +0100788 packet.criteria().forEach(newSelector::add);
Andrea Campanella3970e472018-01-25 16:44:04 +0100789 //FIXME optimize
790 for (Instruction instruction : instructions) {
791 newSelector = translateInstruction(newSelector, instruction);
792 }
Andrea Campanellae4084402017-12-15 15:27:31 +0100793 return newSelector;
794 }
795
796 /**
797 * Applies an instruction to the packet in the form of a selector.
798 *
799 * @param newSelector the packet selector
800 * @param instruction the instruction to be translated
801 * @return the new selector with the applied instruction
802 */
Andrea Campanella8292ba62018-01-31 16:43:23 +0100803 private Builder translateInstruction(Builder newSelector, Instruction instruction) {
Andrea Campanellae4084402017-12-15 15:27:31 +0100804 log.debug("Translating instruction {}", instruction);
Andrea Campanella3970e472018-01-25 16:44:04 +0100805 log.debug("New Selector {}", newSelector.build());
Andrea Campanellae4084402017-12-15 15:27:31 +0100806 //TODO add as required
807 Criterion criterion = null;
808 switch (instruction.type()) {
809 case L2MODIFICATION:
810 L2ModificationInstruction l2Instruction = (L2ModificationInstruction) instruction;
811 switch (l2Instruction.subtype()) {
812 case VLAN_ID:
Andrea Campanella94c594a2018-02-06 18:58:40 +0100813 ModVlanIdInstruction vlanIdInstruction =
814 (ModVlanIdInstruction) instruction;
Andrea Campanellae4084402017-12-15 15:27:31 +0100815 VlanId id = vlanIdInstruction.vlanId();
816 criterion = Criteria.matchVlanId(id);
817 break;
818 case VLAN_POP:
819 criterion = Criteria.matchVlanId(VlanId.NONE);
820 break;
821 case MPLS_PUSH:
Andrea Campanella94c594a2018-02-06 18:58:40 +0100822 ModMplsHeaderInstruction mplsEthInstruction =
823 (ModMplsHeaderInstruction) instruction;
Andrea Campanellae4084402017-12-15 15:27:31 +0100824 criterion = Criteria.matchEthType(mplsEthInstruction.ethernetType().toShort());
825 break;
826 case MPLS_POP:
Andrea Campanella94c594a2018-02-06 18:58:40 +0100827 ModMplsHeaderInstruction mplsPopInstruction =
828 (ModMplsHeaderInstruction) instruction;
Andrea Campanellae4084402017-12-15 15:27:31 +0100829 criterion = Criteria.matchEthType(mplsPopInstruction.ethernetType().toShort());
Andrea Campanella3970e472018-01-25 16:44:04 +0100830
831 //When popping MPLS we remove label and BOS
832 TrafficSelector temporaryPacket = newSelector.build();
833 if (temporaryPacket.getCriterion(Criterion.Type.MPLS_LABEL) != null) {
Andrea Campanella8292ba62018-01-31 16:43:23 +0100834 Builder noMplsSelector = DefaultTrafficSelector.builder();
Andrea Campanella3970e472018-01-25 16:44:04 +0100835 temporaryPacket.criteria().stream().filter(c -> {
836 return !c.type().equals(Criterion.Type.MPLS_LABEL) &&
837 !c.type().equals(Criterion.Type.MPLS_BOS);
838 }).forEach(noMplsSelector::add);
839 newSelector = noMplsSelector;
840 }
841
Andrea Campanellae4084402017-12-15 15:27:31 +0100842 break;
843 case MPLS_LABEL:
Andrea Campanella94c594a2018-02-06 18:58:40 +0100844 ModMplsLabelInstruction mplsLabelInstruction =
845 (ModMplsLabelInstruction) instruction;
Andrea Campanellae4084402017-12-15 15:27:31 +0100846 criterion = Criteria.matchMplsLabel(mplsLabelInstruction.label());
Andrea Campanella3970e472018-01-25 16:44:04 +0100847 newSelector.matchMplsBos(true);
Andrea Campanellae4084402017-12-15 15:27:31 +0100848 break;
849 case ETH_DST:
Andrea Campanella94c594a2018-02-06 18:58:40 +0100850 ModEtherInstruction modEtherDstInstruction =
851 (ModEtherInstruction) instruction;
Andrea Campanellae4084402017-12-15 15:27:31 +0100852 criterion = Criteria.matchEthDst(modEtherDstInstruction.mac());
853 break;
854 case ETH_SRC:
Andrea Campanella94c594a2018-02-06 18:58:40 +0100855 ModEtherInstruction modEtherSrcInstruction =
856 (ModEtherInstruction) instruction;
Andrea Campanellae4084402017-12-15 15:27:31 +0100857 criterion = Criteria.matchEthSrc(modEtherSrcInstruction.mac());
858 break;
859 default:
860 log.debug("Unsupported L2 Instruction");
861 break;
862 }
863 break;
864 default:
865 log.debug("Unsupported Instruction");
866 break;
867 }
868 if (criterion != null) {
869 log.debug("Adding criterion {}", criterion);
870 newSelector.add(criterion);
871 }
872 return newSelector;
873 }
874
875 /**
876 * Finds the rule in the device that mathces the input packet and has the highest priority.
877 *
878 * @param packet the input packet
879 * @param in the connect point the packet comes in from
880 * @param tableId the table to search
881 * @return the flow entry
882 */
883 private FlowEntry matchHighestPriority(TrafficSelector packet, ConnectPoint in, TableId tableId) {
884 //Computing the possible match rules.
885 final Comparator<FlowEntry> comparator = Comparator.comparing(FlowRule::priority);
886 return Lists.newArrayList(flowRuleService.getFlowEntries(in.deviceId()).iterator())
887 .stream()
888 .filter(flowEntry -> {
889 return flowEntry.table().equals(tableId);
890 })
891 .filter(flowEntry -> {
892 return match(packet, flowEntry);
893 }).max(comparator).orElse(null);
894 }
895
896 /**
897 * Matches the packet with the given flow entry.
898 *
899 * @param packet the packet to match
900 * @param flowEntry the flow entry to match the packet against
901 * @return true if the packet matches the flow.
902 */
903 private boolean match(TrafficSelector packet, FlowEntry flowEntry) {
904 //TODO handle MAC matching
905 return flowEntry.selector().criteria().stream().allMatch(criterion -> {
906 Criterion.Type type = criterion.type();
Andrea Campanella128d9c62018-01-31 12:20:48 +0100907 //If the criterion has IP we need to do LPM to establish matching.
Andrea Campanellae4084402017-12-15 15:27:31 +0100908 if (type.equals(Criterion.Type.IPV4_SRC) || type.equals(Criterion.Type.IPV4_DST) ||
909 type.equals(Criterion.Type.IPV6_SRC) || type.equals(Criterion.Type.IPV6_DST)) {
910 IPCriterion ipCriterion = (IPCriterion) criterion;
911 IPCriterion matchCriterion = (IPCriterion) packet.getCriterion(ipCriterion.type());
Andrea Campanella128d9c62018-01-31 12:20:48 +0100912 //if the packet does not have an IPv4 or IPv6 criterion we return false
Andrea Campanellae4084402017-12-15 15:27:31 +0100913 if (matchCriterion == null) {
Andrea Campanella128d9c62018-01-31 12:20:48 +0100914 return false;
Andrea Campanellae4084402017-12-15 15:27:31 +0100915 }
916 try {
917 Subnet subnet = Subnet.createInstance(ipCriterion.ip().toString());
918 return subnet.isInSubnet(matchCriterion.ip().address().toInetAddress());
919 } catch (UnknownHostException e) {
920 return false;
921 }
922 //we check that the packet contains the criterion provided by the flow rule.
923 } else {
924 return packet.criteria().contains(criterion);
925 }
926 });
Simon Hunt026a2872017-11-13 17:09:43 -0800927 }
928}