ONOS-2379 Reactive Fwd improvements prune bad flows from switches when a link goes down
Change-Id: I27de61abc6225d12fd4ba5fa36d58ec4061f9db5
Showing
1 changed file
with
181 additions
and
0 deletions
| ... | @@ -15,6 +15,7 @@ | ... | @@ -15,6 +15,7 @@ |
| 15 | */ | 15 | */ |
| 16 | package org.onosproject.fwd; | 16 | package org.onosproject.fwd; |
| 17 | 17 | ||
| 18 | +import com.google.common.collect.ImmutableSet; | ||
| 18 | import org.apache.felix.scr.annotations.Activate; | 19 | import org.apache.felix.scr.annotations.Activate; |
| 19 | import org.apache.felix.scr.annotations.Component; | 20 | import org.apache.felix.scr.annotations.Component; |
| 20 | import org.apache.felix.scr.annotations.Deactivate; | 21 | import org.apache.felix.scr.annotations.Deactivate; |
| ... | @@ -29,35 +30,51 @@ import org.onlab.packet.IPv4; | ... | @@ -29,35 +30,51 @@ import org.onlab.packet.IPv4; |
| 29 | import org.onlab.packet.IPv6; | 30 | import org.onlab.packet.IPv6; |
| 30 | import org.onlab.packet.Ip4Prefix; | 31 | import org.onlab.packet.Ip4Prefix; |
| 31 | import org.onlab.packet.Ip6Prefix; | 32 | import org.onlab.packet.Ip6Prefix; |
| 33 | +import org.onlab.packet.MacAddress; | ||
| 32 | import org.onlab.packet.TCP; | 34 | import org.onlab.packet.TCP; |
| 33 | import org.onlab.packet.UDP; | 35 | import org.onlab.packet.UDP; |
| 34 | import org.onlab.packet.VlanId; | 36 | import org.onlab.packet.VlanId; |
| 35 | import org.onosproject.cfg.ComponentConfigService; | 37 | import org.onosproject.cfg.ComponentConfigService; |
| 36 | import org.onosproject.core.ApplicationId; | 38 | import org.onosproject.core.ApplicationId; |
| 37 | import org.onosproject.core.CoreService; | 39 | import org.onosproject.core.CoreService; |
| 40 | +import org.onosproject.event.Event; | ||
| 41 | +import org.onosproject.net.ConnectPoint; | ||
| 42 | +import org.onosproject.net.DeviceId; | ||
| 38 | import org.onosproject.net.Host; | 43 | import org.onosproject.net.Host; |
| 39 | import org.onosproject.net.HostId; | 44 | import org.onosproject.net.HostId; |
| 45 | +import org.onosproject.net.Link; | ||
| 40 | import org.onosproject.net.Path; | 46 | import org.onosproject.net.Path; |
| 41 | import org.onosproject.net.PortNumber; | 47 | import org.onosproject.net.PortNumber; |
| 42 | import org.onosproject.net.flow.DefaultTrafficSelector; | 48 | import org.onosproject.net.flow.DefaultTrafficSelector; |
| 43 | import org.onosproject.net.flow.DefaultTrafficTreatment; | 49 | import org.onosproject.net.flow.DefaultTrafficTreatment; |
| 50 | +import org.onosproject.net.flow.FlowEntry; | ||
| 51 | +import org.onosproject.net.flow.FlowRule; | ||
| 44 | import org.onosproject.net.flow.FlowRuleService; | 52 | import org.onosproject.net.flow.FlowRuleService; |
| 45 | import org.onosproject.net.flow.TrafficSelector; | 53 | import org.onosproject.net.flow.TrafficSelector; |
| 46 | import org.onosproject.net.flow.TrafficTreatment; | 54 | import org.onosproject.net.flow.TrafficTreatment; |
| 55 | +import org.onosproject.net.flow.criteria.Criterion; | ||
| 56 | +import org.onosproject.net.flow.criteria.EthCriterion; | ||
| 57 | +import org.onosproject.net.flow.instructions.Instruction; | ||
| 58 | +import org.onosproject.net.flow.instructions.Instructions; | ||
| 47 | import org.onosproject.net.flowobjective.DefaultForwardingObjective; | 59 | import org.onosproject.net.flowobjective.DefaultForwardingObjective; |
| 48 | import org.onosproject.net.flowobjective.FlowObjectiveService; | 60 | import org.onosproject.net.flowobjective.FlowObjectiveService; |
| 49 | import org.onosproject.net.flowobjective.ForwardingObjective; | 61 | import org.onosproject.net.flowobjective.ForwardingObjective; |
| 50 | import org.onosproject.net.host.HostService; | 62 | import org.onosproject.net.host.HostService; |
| 63 | +import org.onosproject.net.link.LinkEvent; | ||
| 51 | import org.onosproject.net.packet.InboundPacket; | 64 | import org.onosproject.net.packet.InboundPacket; |
| 52 | import org.onosproject.net.packet.PacketContext; | 65 | import org.onosproject.net.packet.PacketContext; |
| 53 | import org.onosproject.net.packet.PacketPriority; | 66 | import org.onosproject.net.packet.PacketPriority; |
| 54 | import org.onosproject.net.packet.PacketProcessor; | 67 | import org.onosproject.net.packet.PacketProcessor; |
| 55 | import org.onosproject.net.packet.PacketService; | 68 | import org.onosproject.net.packet.PacketService; |
| 69 | +import org.onosproject.net.topology.TopologyEvent; | ||
| 70 | +import org.onosproject.net.topology.TopologyListener; | ||
| 56 | import org.onosproject.net.topology.TopologyService; | 71 | import org.onosproject.net.topology.TopologyService; |
| 57 | import org.osgi.service.component.ComponentContext; | 72 | import org.osgi.service.component.ComponentContext; |
| 58 | import org.slf4j.Logger; | 73 | import org.slf4j.Logger; |
| 59 | 74 | ||
| 60 | import java.util.Dictionary; | 75 | import java.util.Dictionary; |
| 76 | +import java.util.List; | ||
| 77 | +import java.util.Objects; | ||
| 61 | import java.util.Set; | 78 | import java.util.Set; |
| 62 | 79 | ||
| 63 | import static com.google.common.base.Strings.isNullOrEmpty; | 80 | import static com.google.common.base.Strings.isNullOrEmpty; |
| ... | @@ -155,16 +172,21 @@ public class ReactiveForwarding { | ... | @@ -155,16 +172,21 @@ public class ReactiveForwarding { |
| 155 | "default is false") | 172 | "default is false") |
| 156 | private boolean matchIcmpFields = false; | 173 | private boolean matchIcmpFields = false; |
| 157 | 174 | ||
| 175 | + | ||
| 158 | @Property(name = "ignoreIPv4Multicast", boolValue = false, | 176 | @Property(name = "ignoreIPv4Multicast", boolValue = false, |
| 159 | label = "Ignore (do not forward) IPv4 multicast packets; default is false") | 177 | label = "Ignore (do not forward) IPv4 multicast packets; default is false") |
| 160 | private boolean ignoreIpv4McastPackets = false; | 178 | private boolean ignoreIpv4McastPackets = false; |
| 161 | 179 | ||
| 180 | + private final TopologyListener topologyListener = new InternalTopologyListener(); | ||
| 181 | + | ||
| 182 | + | ||
| 162 | @Activate | 183 | @Activate |
| 163 | public void activate(ComponentContext context) { | 184 | public void activate(ComponentContext context) { |
| 164 | cfgService.registerProperties(getClass()); | 185 | cfgService.registerProperties(getClass()); |
| 165 | appId = coreService.registerApplication("org.onosproject.fwd"); | 186 | appId = coreService.registerApplication("org.onosproject.fwd"); |
| 166 | 187 | ||
| 167 | packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2); | 188 | packetService.addProcessor(processor, PacketProcessor.ADVISOR_MAX + 2); |
| 189 | + topologyService.addListener(topologyListener); | ||
| 168 | readComponentConfiguration(context); | 190 | readComponentConfiguration(context); |
| 169 | requestIntercepts(); | 191 | requestIntercepts(); |
| 170 | 192 | ||
| ... | @@ -177,6 +199,7 @@ public class ReactiveForwarding { | ... | @@ -177,6 +199,7 @@ public class ReactiveForwarding { |
| 177 | withdrawIntercepts(); | 199 | withdrawIntercepts(); |
| 178 | flowRuleService.removeFlowRulesById(appId); | 200 | flowRuleService.removeFlowRulesById(appId); |
| 179 | packetService.removeProcessor(processor); | 201 | packetService.removeProcessor(processor); |
| 202 | + topologyService.removeListener(topologyListener); | ||
| 180 | processor = null; | 203 | processor = null; |
| 181 | log.info("Stopped"); | 204 | log.info("Stopped"); |
| 182 | } | 205 | } |
| ... | @@ -383,6 +406,7 @@ public class ReactiveForwarding { | ... | @@ -383,6 +406,7 @@ public class ReactiveForwarding { |
| 383 | public void process(PacketContext context) { | 406 | public void process(PacketContext context) { |
| 384 | // Stop processing if the packet has been handled, since we | 407 | // Stop processing if the packet has been handled, since we |
| 385 | // can't do any more to it. | 408 | // can't do any more to it. |
| 409 | + | ||
| 386 | if (context.isHandled()) { | 410 | if (context.isHandled()) { |
| 387 | return; | 411 | return; |
| 388 | } | 412 | } |
| ... | @@ -646,4 +670,161 @@ public class ReactiveForwarding { | ... | @@ -646,4 +670,161 @@ public class ReactiveForwarding { |
| 646 | packetOut(context, portNumber); | 670 | packetOut(context, portNumber); |
| 647 | } | 671 | } |
| 648 | } | 672 | } |
| 673 | + | ||
| 674 | + private class InternalTopologyListener implements TopologyListener { | ||
| 675 | + @Override | ||
| 676 | + public void event(TopologyEvent event) { | ||
| 677 | + List<Event> reasons = event.reasons(); | ||
| 678 | + if (reasons != null) { | ||
| 679 | + reasons.forEach(re -> { | ||
| 680 | + if (re instanceof LinkEvent) { | ||
| 681 | + LinkEvent le = (LinkEvent) re; | ||
| 682 | + if (le.type() == LinkEvent.Type.LINK_REMOVED) { | ||
| 683 | + fixBlackhole(le.subject().src()); | ||
| 684 | + } | ||
| 685 | + } | ||
| 686 | + }); | ||
| 687 | + } | ||
| 688 | + } | ||
| 689 | + } | ||
| 690 | + | ||
| 691 | + private void fixBlackhole(ConnectPoint egress) { | ||
| 692 | + Set<FlowEntry> rules = getFlowRulesFrom(egress); | ||
| 693 | + Set<SrcDstPair> pairs = findSrcDstPairs(rules); | ||
| 694 | + | ||
| 695 | + for (SrcDstPair sd: pairs) { | ||
| 696 | + // get the edge deviceID for the src host | ||
| 697 | + DeviceId srcId = hostService.getHost(HostId.hostId(sd.src)).location().deviceId(); | ||
| 698 | + DeviceId dstId = hostService.getHost(HostId.hostId(sd.dst)).location().deviceId(); | ||
| 699 | + log.trace("SRC ID is " + srcId + ", DST ID is " + dstId); | ||
| 700 | + | ||
| 701 | + cleanFlowRules(sd, egress.deviceId()); | ||
| 702 | + | ||
| 703 | + Set<Path> shortestPaths = | ||
| 704 | + topologyService.getPaths(topologyService.currentTopology(), egress.deviceId(), srcId); | ||
| 705 | + backTrackBadNodes(shortestPaths, dstId, sd); | ||
| 706 | + } | ||
| 707 | + } | ||
| 708 | + | ||
| 709 | + // Backtracks from link down event to remove flows that lead to blackhole | ||
| 710 | + private void backTrackBadNodes(Set<Path> shortestPaths, DeviceId dstId, SrcDstPair sd) { | ||
| 711 | + for (Path p: shortestPaths) { | ||
| 712 | + List<Link> pathLinks = p.links(); | ||
| 713 | + for (int i = 0; i < pathLinks.size(); i = i + 1) { | ||
| 714 | + Link curLink = pathLinks.get(i); | ||
| 715 | + DeviceId curDevice = curLink.src().deviceId(); | ||
| 716 | + log.trace("Currently inspecting device: " + curDevice); | ||
| 717 | + | ||
| 718 | + // skipping the first link because this link's src has already been pruned beforehand | ||
| 719 | + if (i != 0) { | ||
| 720 | + cleanFlowRules(sd, curDevice); | ||
| 721 | + } | ||
| 722 | + | ||
| 723 | + Set<Path> pathsFromCurDevice = topologyService.getPaths(topologyService.currentTopology(), | ||
| 724 | + curDevice, dstId); | ||
| 725 | + if (pickForwardPath(pathsFromCurDevice, curLink.src().port()) != null) { | ||
| 726 | + break; | ||
| 727 | + } else { | ||
| 728 | + if (i + 1 == pathLinks.size()) { | ||
| 729 | + cleanFlowRules(sd, curLink.dst().deviceId()); | ||
| 730 | + } | ||
| 731 | + } | ||
| 732 | + } | ||
| 733 | + } | ||
| 734 | + } | ||
| 735 | + | ||
| 736 | + // Removes flow rules off specified device with specific SrcDstPair | ||
| 737 | + private void cleanFlowRules(SrcDstPair pair, DeviceId id) { | ||
| 738 | + log.trace("Searching for flow rules to remove from: " + id); | ||
| 739 | + log.trace("Removing flows w/ SRC=" + pair.src + ", DST=" + pair.dst); | ||
| 740 | + for (FlowEntry r : flowRuleService.getFlowEntries(id)) { | ||
| 741 | + boolean matchesSrc = false, matchesDst = false; | ||
| 742 | + for (Instruction i : r.treatment().allInstructions()) { | ||
| 743 | + if (i.type() == Instruction.Type.OUTPUT) { | ||
| 744 | + //if the flow has matching src and dst | ||
| 745 | + for (Criterion cr : r.selector().criteria()) { | ||
| 746 | + if (cr.type() == Criterion.Type.ETH_DST) { | ||
| 747 | + if (((EthCriterion) cr).mac().equals(pair.dst)) { | ||
| 748 | + matchesDst = true; | ||
| 749 | + } | ||
| 750 | + } else if (cr.type() == Criterion.Type.ETH_SRC) { | ||
| 751 | + if (((EthCriterion) cr).mac().equals(pair.src)) { | ||
| 752 | + matchesSrc = true; | ||
| 753 | + } | ||
| 754 | + } | ||
| 755 | + } | ||
| 756 | + } | ||
| 757 | + } | ||
| 758 | + if (matchesDst && matchesSrc) { | ||
| 759 | + log.trace("Removed flow rule from device: " + id); | ||
| 760 | + flowRuleService.removeFlowRules((FlowRule) r); | ||
| 761 | + } | ||
| 762 | + } | ||
| 763 | + | ||
| 764 | + } | ||
| 765 | + | ||
| 766 | + // Returns a set of src/dst MAC pairs extracted from the specified set of flow entries | ||
| 767 | + private Set<SrcDstPair> findSrcDstPairs(Set<FlowEntry> rules) { | ||
| 768 | + ImmutableSet.Builder<SrcDstPair> builder = ImmutableSet.builder(); | ||
| 769 | + for (FlowEntry r: rules) { | ||
| 770 | + MacAddress src = null, dst = null; | ||
| 771 | + for (Criterion cr: r.selector().criteria()) { | ||
| 772 | + if (cr.type() == Criterion.Type.ETH_DST) { | ||
| 773 | + dst = ((EthCriterion) cr).mac(); | ||
| 774 | + } else if (cr.type() == Criterion.Type.ETH_SRC) { | ||
| 775 | + src = ((EthCriterion) cr).mac(); | ||
| 776 | + } | ||
| 777 | + } | ||
| 778 | + builder.add(new SrcDstPair(src, dst)); | ||
| 779 | + } | ||
| 780 | + return builder.build(); | ||
| 781 | + } | ||
| 782 | + | ||
| 783 | + // Returns set of flowEntries which were created by this application and which egress from the | ||
| 784 | + // specified connection port | ||
| 785 | + private Set<FlowEntry> getFlowRulesFrom(ConnectPoint egress) { | ||
| 786 | + ImmutableSet.Builder<FlowEntry> builder = ImmutableSet.builder(); | ||
| 787 | + flowRuleService.getFlowEntries(egress.deviceId()).forEach(r -> { | ||
| 788 | + if (r.appId() == appId.id()) { | ||
| 789 | + r.treatment().allInstructions().forEach(i -> { | ||
| 790 | + if (i.type() == Instruction.Type.OUTPUT) { | ||
| 791 | + if (((Instructions.OutputInstruction) i).port().equals(egress.port())) { | ||
| 792 | + builder.add(r); | ||
| 793 | + } | ||
| 794 | + } | ||
| 795 | + }); | ||
| 796 | + } | ||
| 797 | + }); | ||
| 798 | + | ||
| 799 | + return builder.build(); | ||
| 800 | + } | ||
| 801 | + | ||
| 802 | + // Wrapper class for a source and destination pair of MAC addresses | ||
| 803 | + private final class SrcDstPair { | ||
| 804 | + final MacAddress src; | ||
| 805 | + final MacAddress dst; | ||
| 806 | + | ||
| 807 | + private SrcDstPair(MacAddress src, MacAddress dst) { | ||
| 808 | + this.src = src; | ||
| 809 | + this.dst = dst; | ||
| 810 | + } | ||
| 811 | + | ||
| 812 | + @Override | ||
| 813 | + public boolean equals(Object o) { | ||
| 814 | + if (this == o) { | ||
| 815 | + return true; | ||
| 816 | + } | ||
| 817 | + if (o == null || getClass() != o.getClass()) { | ||
| 818 | + return false; | ||
| 819 | + } | ||
| 820 | + SrcDstPair that = (SrcDstPair) o; | ||
| 821 | + return Objects.equals(src, that.src) && | ||
| 822 | + Objects.equals(dst, that.dst); | ||
| 823 | + } | ||
| 824 | + | ||
| 825 | + @Override | ||
| 826 | + public int hashCode() { | ||
| 827 | + return Objects.hash(src, dst); | ||
| 828 | + } | ||
| 829 | + } | ||
| 649 | } | 830 | } | ... | ... |
-
Please register or login to post a comment