blob: 1a5c946c0e5808bfc96004bd2510d78c35d79df3 [file] [log] [blame]
Simon Hunt6fefd852017-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 Campanella01e886e2017-12-15 15:27:31 +010019import com.google.common.collect.ImmutableList;
20import com.google.common.collect.Lists;
Simon Hunt6fefd852017-11-13 17:09:43 -080021import org.apache.felix.scr.annotations.Component;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
Andrea Campanella01e886e2017-12-15 15:27:31 +010025import org.onlab.packet.VlanId;
Simon Hunt6fefd852017-11-13 17:09:43 -080026import org.onosproject.net.ConnectPoint;
Andrea Campanella01e886e2017-12-15 15:27:31 +010027import org.onosproject.net.DeviceId;
28import org.onosproject.net.Host;
29import org.onosproject.net.Link;
30import org.onosproject.net.PortNumber;
31import org.onosproject.net.driver.DriverService;
32import org.onosproject.net.flow.DefaultTrafficSelector;
33import org.onosproject.net.flow.FlowEntry;
34import org.onosproject.net.flow.FlowRule;
Simon Hunt6fefd852017-11-13 17:09:43 -080035import org.onosproject.net.flow.FlowRuleService;
Andrea Campanella01e886e2017-12-15 15:27:31 +010036import org.onosproject.net.flow.IndexTableId;
37import org.onosproject.net.flow.TableId;
Simon Hunt6fefd852017-11-13 17:09:43 -080038import org.onosproject.net.flow.TrafficSelector;
Andrea Campanella01e886e2017-12-15 15:27:31 +010039import org.onosproject.net.flow.criteria.Criteria;
40import org.onosproject.net.flow.criteria.Criterion;
41import org.onosproject.net.flow.criteria.EthCriterion;
42import org.onosproject.net.flow.criteria.EthTypeCriterion;
43import org.onosproject.net.flow.criteria.IPCriterion;
44import org.onosproject.net.flow.instructions.Instruction;
45import org.onosproject.net.flow.instructions.Instructions;
46import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
47import org.onosproject.net.flow.instructions.L2ModificationInstruction;
48import org.onosproject.net.group.Group;
49import org.onosproject.net.group.GroupBucket;
Simon Hunt6fefd852017-11-13 17:09:43 -080050import org.onosproject.net.group.GroupService;
Andrea Campanella01e886e2017-12-15 15:27:31 +010051import org.onosproject.net.host.HostService;
52import org.onosproject.net.link.LinkService;
53import org.onosproject.t3.api.GroupsInDevice;
Simon Hunt6fefd852017-11-13 17:09:43 -080054import org.onosproject.t3.api.StaticPacketTrace;
55import org.onosproject.t3.api.TroubleshootService;
56import org.slf4j.Logger;
57
Andrea Campanella01e886e2017-12-15 15:27:31 +010058import java.net.UnknownHostException;
59import java.util.ArrayList;
60import java.util.Collections;
61import java.util.Comparator;
62import java.util.HashSet;
63import java.util.List;
64import java.util.Set;
65import java.util.stream.Collectors;
66
67import static org.onlab.packet.EthType.EtherType;
68import static org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
Simon Hunt6fefd852017-11-13 17:09:43 -080069import static org.slf4j.LoggerFactory.getLogger;
70
71/**
Andrea Campanella01e886e2017-12-15 15:27:31 +010072 * Manager to troubleshoot packets inside the network.
73 * Given a representation of a packet follows it's path in the network according to the existing flows and groups in
74 * the devices.
Simon Hunt6fefd852017-11-13 17:09:43 -080075 */
76@Service
77@Component(immediate = true)
78public class TroubleshootManager implements TroubleshootService {
79
80 private static final Logger log = getLogger(TroubleshootManager.class);
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected FlowRuleService flowRuleService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected GroupService groupService;
87
Andrea Campanella01e886e2017-12-15 15:27:31 +010088 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected LinkService linkService;
Simon Hunt6fefd852017-11-13 17:09:43 -080090
Andrea Campanella01e886e2017-12-15 15:27:31 +010091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected HostService hostService;
93
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected DriverService driverService;
Simon Hunt6fefd852017-11-13 17:09:43 -080096
97 @Override
98 public StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in) {
Andrea Campanella01e886e2017-12-15 15:27:31 +010099 log.info("Tracing packet {} coming in through {}", packet, in);
100 StaticPacketTrace trace = new StaticPacketTrace(packet, in);
101 //FIXME this can be done recursively
102 trace = traceInDevice(trace, packet, in);
103 //Building output connect Points
104 List<ConnectPoint> path = new ArrayList<>();
105 trace = getTrace(path, in, trace);
106 return trace;
107 }
108
109 /**
110 * Computes a trace for a give packet that start in the network at the given connect point.
111 *
112 * @param completePath the path traversed by the packet
113 * @param in the input connect point
114 * @param trace the trace to build
115 * @return the build trace for that packet.
116 */
117 private StaticPacketTrace getTrace(List<ConnectPoint> completePath, ConnectPoint in, StaticPacketTrace trace) {
118
119 //if the trace already contains the input connect point there is a loop
120 if (pathContainsDevice(completePath, in.deviceId())) {
121 trace.addResultMessage("Loop encountered in device " + in.deviceId());
122 return trace;
123 }
124
125 //let's add the input connect point
126 completePath.add(in);
127
128 //If the trace has no outputs for the given input we stop here
129 if (trace.getGroupOuputs(in.deviceId()) == null) {
130 computePath(completePath, trace, null);
131 trace.addResultMessage("No output out of device " + in.deviceId() + ". Packet is dropped");
132 return trace;
133 }
134 //If the trace has ouputs we analyze them all
135 for (GroupsInDevice outputPath : trace.getGroupOuputs(in.deviceId())) {
136 log.debug("Output path {}", outputPath.getOutput());
137 //Hosts for the the given output
138 Set<Host> hostsList = hostService.getConnectedHosts(outputPath.getOutput());
139 //Hosts queried from the original ip or mac
140 Set<Host> hosts = getHosts(trace);
141
142 //If the two host collections contain the same item it means we reached the proper output
143 if (!Collections.disjoint(hostsList, hosts)) {
144 log.debug("Stopping here because host is expected destination");
145 trace.addResultMessage("Reached required destination Host");
146 computePath(completePath, trace, outputPath.getOutput());
147 break;
148 } else {
149 ConnectPoint cp = outputPath.getOutput();
150 //let's add the ouput for the input
151 completePath.add(cp);
152 log.debug("------------------------------------------------------------");
153 log.debug("Connect Point out {}", cp);
154 //let's compute the links for the given output
155 Set<Link> links = linkService.getEgressLinks(cp);
156 log.debug("Egress Links {}", links);
157 //No links means that the packet gets dropped.
158 if (links.size() == 0) {
159 log.warn("No links out of {}", cp);
160 computePath(completePath, trace, cp);
161 trace.addResultMessage("No links depart from " + cp + ". Packet is dropped");
162 return trace;
163 }
164 //For each link we trace the corresponding device
165 for (Link link : links) {
166 ConnectPoint dst = link.dst();
167 //change in-port to the dst link in port
168 TrafficSelector.Builder updatedPacket = DefaultTrafficSelector.builder();
169 outputPath.getFinalPacket().criteria().forEach(updatedPacket::add);
170 updatedPacket.add(Criteria.matchInPort(dst.port()));
171 log.debug("DST Connect Point {}", dst);
172 //build the elements for that device
173 traceInDevice(trace, updatedPacket.build(), dst);
174 //continue the trace along the path
175 getTrace(completePath, dst, trace);
176 }
177
178 }
179 }
180 return trace;
181 }
182
183 /**
184 * Checks if the path contains the device.
185 *
186 * @param completePath the path
187 * @param deviceId the device to check
188 * @return true if the path contains the device
189 */
190 //TODO might prove costly, improvement: a class with both CPs and DeviceIds point.
191 private boolean pathContainsDevice(List<ConnectPoint> completePath, DeviceId deviceId) {
192 for (ConnectPoint cp : completePath) {
193 if (cp.deviceId().equals(deviceId)) {
194 return true;
195 }
196 }
197 return false;
198 }
199
200 /**
201 * Gets the hosts for the given initial packet.
202 *
203 * @param trace the trace we are building
204 * @return set of the hosts we are trying to reach
205 */
206 private Set<Host> getHosts(StaticPacketTrace trace) {
207 IPCriterion ipv4Criterion = ((IPCriterion) trace.getInitialPacket()
208 .getCriterion(Criterion.Type.IPV4_DST));
209 IPCriterion ipv6Criterion = ((IPCriterion) trace.getInitialPacket()
210 .getCriterion(Criterion.Type.IPV6_DST));
211 Set<Host> hosts = new HashSet<>();
212 if (ipv4Criterion != null) {
213 hosts.addAll(hostService.getHostsByIp(ipv4Criterion.ip().address()));
214 }
215 if (ipv6Criterion != null) {
216 hosts.addAll(hostService.getHostsByIp(ipv6Criterion.ip().address()));
217 }
218 EthCriterion ethCriterion = ((EthCriterion) trace.getInitialPacket()
219 .getCriterion(Criterion.Type.ETH_DST));
220 if (ethCriterion != null) {
221 hosts.addAll(hostService.getHostsByMac(ethCriterion.mac()));
222 }
223 return hosts;
224 }
225
226 /**
227 * Computes the list of traversed connect points.
228 *
229 * @param completePath the list of devices
230 * @param trace the trace we are building
231 * @param output the final output connect point
232 */
233 private void computePath(List<ConnectPoint> completePath, StaticPacketTrace trace, ConnectPoint output) {
234 List<ConnectPoint> traverseList = new ArrayList<>();
235 if (!completePath.contains(trace.getInitialConnectPoint())) {
236 traverseList.add(trace.getInitialConnectPoint());
237 }
238 traverseList.addAll(completePath);
239 if (output != null && !completePath.contains(output)) {
240 traverseList.add(output);
241 }
242 trace.addCompletePath(traverseList);
243 completePath.clear();
244 }
245
246 /**
247 * Traces the packet inside a device starting from an input connect point.
248 *
249 * @param trace the trace we are building
250 * @param packet the packet we are tracing
251 * @param in the input connect point.
252 * @return updated trace
253 */
254 private StaticPacketTrace traceInDevice(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in) {
255 log.debug("Packet {} coming in from {}", packet, in);
256 List<FlowEntry> flows = new ArrayList<>();
257 List<FlowEntry> outputFlows = new ArrayList<>();
258
259 FlowEntry nextTableIdEntry = findNextTableIdEntry(in.deviceId(), -1);
260 if (nextTableIdEntry == null) {
261 trace.addResultMessage("No flow rules for device " + in.deviceId() + ". Aborting");
262 return trace;
263 }
264 TableId tableId = nextTableIdEntry.table();
265 FlowEntry flowEntry;
266 boolean output = false;
267 while (!output) {
268 log.debug("Searching a Flow Entry on table {} for packet {}", tableId, packet);
269 //get the rule that matches the incoming packet
270 flowEntry = matchHighestPriority(packet, in, tableId);
271 log.debug("Found Flow Entry {}", flowEntry);
272
273 boolean isOfdpaHardware = TroubleshootUtils.hardwareOfdpaMap
274 .getOrDefault(driverService.getDriver(in.deviceId()).name(), false);
275
276 //if the flow entry on a table is null and we are on hardware we treat as table miss, with few exceptions
277 if (flowEntry == null && isOfdpaHardware) {
278 log.debug("Ofdpa Hw setup, no flow rule means table miss");
279
280 //Handling Hardware Specifics
281 if (((IndexTableId) tableId).id() == 27) {
282 //Apparently a miss but Table 27 on OFDPA is a fixed table
283 packet = handleOfdpa27FixedTable(trace, packet);
284 }
285
286 //Finding next table to go In case of miss
287 nextTableIdEntry = findNextTableIdEntry(in.deviceId(), ((IndexTableId) tableId).id());
288 log.debug("Next table id entry {}", nextTableIdEntry);
289
290 //FIXME find better solution that enable granularity greater than 0 or all rules
291 //(another possibility is max tableId)
292 if (nextTableIdEntry == null && flows.size() == 0) {
293 trace.addResultMessage("No flow rules for device" + in.deviceId() + ". Aborting");
294 return trace;
295
296 } else if (nextTableIdEntry == null) {
297 //Means that no more flow rules are present
298 output = true;
299
300 } else if (((IndexTableId) tableId).id() == 20) {
301 //if the table is 20 OFDPA skips to table 50
302 log.debug("A miss on Table 20 on OFDPA means that we skip directly to table 50");
303 tableId = IndexTableId.of(50);
304
305 } else {
306 tableId = nextTableIdEntry.table();
307 }
308
309
310 } else if (flowEntry == null) {
311 trace.addResultMessage("Packet has no match on table " + tableId + " in device " +
312 in.deviceId() + ". Dropping");
313 return trace;
314 } else {
315 //IF the table has a transition
316 if (flowEntry.treatment().tableTransition() != null) {
317 //update the next table we transitions to
318 tableId = IndexTableId.of(flowEntry.treatment().tableTransition().tableId());
319 log.debug("Flow Entry has transition to table Id {}", tableId);
320 flows.add(flowEntry);
321 } else {
322 //table has no transition so it means that it's an output rule if on the last table
323 log.debug("Flow Entry has no transition to table, treating as last rule {}", flowEntry);
324 flows.add(flowEntry);
325 outputFlows.add(flowEntry);
326 output = true;
327 }
328 //update the packet according to the actions of this flow rule.
329 packet = updatePacket(packet, flowEntry.treatment().allInstructions()).build();
330 }
331 }
332
333 //Creating a modifiable builder for the output packet
334 TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
335 packet.criteria().forEach(builder::add);
336 //Adding all the flows to the trace
337 trace.addFlowsForDevice(in.deviceId(), flows);
338
339 log.debug("Flows traversed by {}", packet);
340 flows.forEach(entry -> {
341 log.debug("Flow {}", entry);
342 });
343
344 log.debug("Output Flows for {}", packet);
345 outputFlows.forEach(entry -> {
346 log.debug("Output Flow {}", entry);
347 });
348 List<PortNumber> outputPorts = new ArrayList<>();
349
350 //Decide Output for packet when flow rule contains an OUTPUT instruction
351 Set<Instruction> outputFlowEntries = outputFlows.stream().flatMap(flow -> {
352 return flow.treatment().allInstructions().stream();
353 })
354 .filter(instruction -> {
355 return instruction.type().equals(Instruction.Type.OUTPUT);
356 }).collect(Collectors.toSet());
357 log.debug("Output instructions {}", outputFlowEntries);
358
359 if (outputFlowEntries.size() != 0) {
360 outputThroughFlows(trace, packet, in, builder, outputPorts, outputFlowEntries);
361
362 } else {
363 log.debug("Handling Groups");
364 //Analyze Groups
365 List<Group> groups = new ArrayList<>();
366
367 for (FlowEntry entry : flows) {
368 getGroupsFromInstructions(trace, groups, entry.treatment().allInstructions(),
369 entry.deviceId(), builder, outputPorts);
370 }
371 packet = builder.build();
372 log.debug("Groups hit by packet {}", packet);
373 groups.forEach(group -> {
374 log.debug("Group {}", group);
375 });
376 }
377 log.debug("Output ports for packet {}", packet);
378 outputPorts.forEach(port -> {
379 log.debug("Port {}", port);
380 });
381 log.debug("Output Packet {}", packet);
382 return trace;
383 }
384
385 /**
386 * Method that saves the output if that si done via an OUTPUT treatment of a flow rule.
387 *
388 * @param trace the trace
389 * @param packet the packet coming in to this device
390 * @param in the input connect point for this device
391 * @param builder the updated packet0
392 * @param outputPorts the list of output ports for this device
393 * @param outputFlowEntries the list of flow entries with OUTPUT treatment
394 */
395 private void outputThroughFlows(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in,
396 TrafficSelector.Builder builder, List<PortNumber> outputPorts,
397 Set<Instruction> outputFlowEntries) {
398 if (outputFlowEntries.size() > 1) {
399 log.warn("There cannot be more than one OUTPUT instruction for {}", packet);
400 } else {
401 OutputInstruction outputInstruction = (OutputInstruction) outputFlowEntries.iterator().next();
402 //FIXME using GroupsInDevice for output even if flows.
403 trace.addGroupOutputPath(in.deviceId(),
404 new GroupsInDevice(ConnectPoint.deviceConnectPoint(in.deviceId()
405 + "/" + outputInstruction.port()),
406 ImmutableList.of(), builder.build()));
407 outputPorts.add(outputInstruction.port());
408 }
409 }
410
411 /**
412 * Handles table 27 in Ofpda which is a fixed table not visible to any controller that handles Mpls Labels.
413 *
414 * @param packet the incoming packet
415 * @return the updated packet
416 */
417 private TrafficSelector handleOfdpa27FixedTable(StaticPacketTrace trace, TrafficSelector packet) {
418 log.debug("Handling table 27 on OFDPA, removing mpls ETH Type and change mpls label");
419 Criterion mplsCriterion = packet.getCriterion(Criterion.Type.ETH_TYPE);
420 ImmutableList.Builder<Instruction> builder = ImmutableList.builder();
421
422 //If the pakcet comes in with the expected elements we update it as per OFDPA spec.
423 if (mplsCriterion != null && ((EthTypeCriterion) mplsCriterion).ethType()
424 .equals(EtherType.MPLS_UNICAST.ethType())) {
425 Instruction ethInstruction = Instructions.popMpls(((EthTypeCriterion) trace.getInitialPacket()
426 .getCriterion(Criterion.Type.ETH_TYPE)).ethType());
427 //FIXME what do we use as L3_Unicast mpls Label ?
428 builder.add(ethInstruction);
429 }
430 packet = updatePacket(packet, builder.build()).build();
431 return packet;
432 }
433
434 /**
435 * Finds the flow entry with the minimun next table Id.
436 *
437 * @param deviceId the device to search
438 * @param currentId the current id. the search will use this as minimum
439 * @return the flow entry with the minimum table Id after the given one.
440 */
441 private FlowEntry findNextTableIdEntry(DeviceId deviceId, int currentId) {
442
443 final Comparator<FlowEntry> comparator = Comparator.comparing((FlowEntry f) -> ((IndexTableId) f.table()).id());
444
445 return Lists.newArrayList(flowRuleService.getFlowEntries(deviceId).iterator())
446 .stream().filter(f -> ((IndexTableId) f.table()).id() > currentId).min(comparator).orElse(null);
447 }
448
449 /**
450 * Gets group information from instructions.
451 *
452 * @param trace the trace we are building
453 * @param groupsForDevice the set of groups for this device
454 * @param instructions the set of instructions we are searching for groups.
455 * @param deviceId the device we are considering
456 * @param builder the builder of the input packet
457 * @param outputPorts the output ports for that packet
458 */
459 private void getGroupsFromInstructions(StaticPacketTrace trace, List<Group> groupsForDevice,
460 List<Instruction> instructions, DeviceId deviceId,
461 TrafficSelector.Builder builder, List<PortNumber> outputPorts) {
462 List<Instruction> groupInstructionlist = new ArrayList<>();
463 for (Instruction instruction : instructions) {
464 log.debug("Considering Instruction {}", instruction);
465 //if the instruction is not group we need to update the packet or add the output
466 //to the possible outputs for this packet
467 if (!instruction.type().equals(Instruction.Type.GROUP)) {
468 //if the instruction is not group we need to update the packet or add the output
469 //to the possible outputs for this packet
470 if (instruction.type().equals(Instruction.Type.OUTPUT)) {
471 outputPorts.add(((OutputInstruction) instruction).port());
472 trace.addGroupOutputPath(deviceId,
473 new GroupsInDevice(ConnectPoint.deviceConnectPoint(deviceId + "/" +
474 ((OutputInstruction) instruction).port()),
475 groupsForDevice, builder.build()));
476 } else {
477 builder = translateInstruction(builder, instruction);
478 }
479 } else {
480 //if the instuction is pointing to a group we need to get the group
481 groupInstructionlist.add(instruction);
482 }
483 }
484 //handle all the internal instructions pointing to a group.
485 for (Instruction instr : groupInstructionlist) {
486 GroupInstruction groupInstruction = (GroupInstruction) instr;
487 Group group = Lists.newArrayList(groupService.getGroups(deviceId)).stream().filter(groupInternal -> {
488 return groupInternal.id().equals(groupInstruction.groupId());
489 }).findAny().orElse(null);
490 if (group == null) {
491 trace.addResultMessage("Null group for Instruction " + instr);
492 break;
493 }
494 //add the group to the traversed groups
495 groupsForDevice.add(group);
496 //Cycle in each of the group's buckets and add them to the groups for this Device.
497 for (GroupBucket bucket : group.buckets().buckets()) {
498 getGroupsFromInstructions(trace, groupsForDevice, bucket.treatment().allInstructions(),
499 deviceId, builder, outputPorts);
500 }
501 }
502 }
503
504 /**
505 * Applies all give instructions to the input packet.
506 *
507 * @param packet the input packet
508 * @param instructions the set of instructions
509 * @return the packet with the applied instructions
510 */
511 private TrafficSelector.Builder updatePacket(TrafficSelector packet, List<Instruction> instructions) {
512 TrafficSelector.Builder newSelector = DefaultTrafficSelector.builder();
513 packet.criteria().forEach(newSelector::add);
514 instructions.forEach(instruction -> {
515 translateInstruction(newSelector, instruction);
516 });
517 return newSelector;
518 }
519
520 /**
521 * Applies an instruction to the packet in the form of a selector.
522 *
523 * @param newSelector the packet selector
524 * @param instruction the instruction to be translated
525 * @return the new selector with the applied instruction
526 */
527 private TrafficSelector.Builder translateInstruction(TrafficSelector.Builder newSelector, Instruction instruction) {
528 log.debug("Translating instruction {}", instruction);
529 //TODO add as required
530 Criterion criterion = null;
531 switch (instruction.type()) {
532 case L2MODIFICATION:
533 L2ModificationInstruction l2Instruction = (L2ModificationInstruction) instruction;
534 switch (l2Instruction.subtype()) {
535 case VLAN_ID:
536 L2ModificationInstruction.ModVlanIdInstruction vlanIdInstruction =
537 (L2ModificationInstruction.ModVlanIdInstruction) instruction;
538 VlanId id = vlanIdInstruction.vlanId();
539 criterion = Criteria.matchVlanId(id);
540 break;
541 case VLAN_POP:
542 criterion = Criteria.matchVlanId(VlanId.NONE);
543 break;
544 case MPLS_PUSH:
545 L2ModificationInstruction.ModMplsHeaderInstruction mplsEthInstruction =
546 (L2ModificationInstruction.ModMplsHeaderInstruction) instruction;
547 criterion = Criteria.matchEthType(mplsEthInstruction.ethernetType().toShort());
548 break;
549 case MPLS_POP:
550 L2ModificationInstruction.ModMplsHeaderInstruction mplsPopInstruction =
551 (L2ModificationInstruction.ModMplsHeaderInstruction) instruction;
552 criterion = Criteria.matchEthType(mplsPopInstruction.ethernetType().toShort());
553 break;
554 case MPLS_LABEL:
555 L2ModificationInstruction.ModMplsLabelInstruction mplsLabelInstruction =
556 (L2ModificationInstruction.ModMplsLabelInstruction) instruction;
557 criterion = Criteria.matchMplsLabel(mplsLabelInstruction.label());
558 break;
559 case ETH_DST:
560 L2ModificationInstruction.ModEtherInstruction modEtherDstInstruction =
561 (L2ModificationInstruction.ModEtherInstruction) instruction;
562 criterion = Criteria.matchEthDst(modEtherDstInstruction.mac());
563 break;
564 case ETH_SRC:
565 L2ModificationInstruction.ModEtherInstruction modEtherSrcInstruction =
566 (L2ModificationInstruction.ModEtherInstruction) instruction;
567 criterion = Criteria.matchEthSrc(modEtherSrcInstruction.mac());
568 break;
569 default:
570 log.debug("Unsupported L2 Instruction");
571 break;
572 }
573 break;
574 default:
575 log.debug("Unsupported Instruction");
576 break;
577 }
578 if (criterion != null) {
579 log.debug("Adding criterion {}", criterion);
580 newSelector.add(criterion);
581 }
582 return newSelector;
583 }
584
585 /**
586 * Finds the rule in the device that mathces the input packet and has the highest priority.
587 *
588 * @param packet the input packet
589 * @param in the connect point the packet comes in from
590 * @param tableId the table to search
591 * @return the flow entry
592 */
593 private FlowEntry matchHighestPriority(TrafficSelector packet, ConnectPoint in, TableId tableId) {
594 //Computing the possible match rules.
595 final Comparator<FlowEntry> comparator = Comparator.comparing(FlowRule::priority);
596 return Lists.newArrayList(flowRuleService.getFlowEntries(in.deviceId()).iterator())
597 .stream()
598 .filter(flowEntry -> {
599 return flowEntry.table().equals(tableId);
600 })
601 .filter(flowEntry -> {
602 return match(packet, flowEntry);
603 }).max(comparator).orElse(null);
604 }
605
606 /**
607 * Matches the packet with the given flow entry.
608 *
609 * @param packet the packet to match
610 * @param flowEntry the flow entry to match the packet against
611 * @return true if the packet matches the flow.
612 */
613 private boolean match(TrafficSelector packet, FlowEntry flowEntry) {
614 //TODO handle MAC matching
615 return flowEntry.selector().criteria().stream().allMatch(criterion -> {
616 Criterion.Type type = criterion.type();
617 //If the critrion has IP we need to do LPM to establish matching.
618 if (type.equals(Criterion.Type.IPV4_SRC) || type.equals(Criterion.Type.IPV4_DST) ||
619 type.equals(Criterion.Type.IPV6_SRC) || type.equals(Criterion.Type.IPV6_DST)) {
620 IPCriterion ipCriterion = (IPCriterion) criterion;
621 IPCriterion matchCriterion = (IPCriterion) packet.getCriterion(ipCriterion.type());
622 //if the packet does not have an IPv4 or IPv6 criterion we return true
623 if (matchCriterion == null) {
624 return true;
625 }
626 try {
627 Subnet subnet = Subnet.createInstance(ipCriterion.ip().toString());
628 return subnet.isInSubnet(matchCriterion.ip().address().toInetAddress());
629 } catch (UnknownHostException e) {
630 return false;
631 }
632 //we check that the packet contains the criterion provided by the flow rule.
633 } else {
634 return packet.criteria().contains(criterion);
635 }
636 });
Simon Hunt6fefd852017-11-13 17:09:43 -0800637 }
638}