Segment Routing bug fix and enhancement
Bugfix: - Add MPLS BOS matching - Fix NPE caused by race between filter objective and broadcast next objective Enhancement: - Move group handler out from OFDPA pipeline - Move ARP request from rule populator to packet request Change-Id: I0ba40e10f7cb7f97277df86725fbd2546a62e890
Showing
12 changed files
with
1265 additions
and
1206 deletions
... | @@ -513,7 +513,6 @@ public class DefaultRoutingHandler { | ... | @@ -513,7 +513,6 @@ public class DefaultRoutingHandler { |
513 | public void populatePortAddressingRules(DeviceId deviceId) { | 513 | public void populatePortAddressingRules(DeviceId deviceId) { |
514 | rulePopulator.populateRouterMacVlanFilters(deviceId); | 514 | rulePopulator.populateRouterMacVlanFilters(deviceId); |
515 | rulePopulator.populateRouterIpPunts(deviceId); | 515 | rulePopulator.populateRouterIpPunts(deviceId); |
516 | - rulePopulator.populateArpPunts(deviceId); | ||
517 | } | 516 | } |
518 | 517 | ||
519 | /** | 518 | /** | ... | ... |
... | @@ -303,6 +303,7 @@ public class RoutingRulePopulator { | ... | @@ -303,6 +303,7 @@ public class RoutingRulePopulator { |
303 | // TODO Handle the case of Bos == false | 303 | // TODO Handle the case of Bos == false |
304 | sbuilder.matchEthType(Ethernet.MPLS_UNICAST); | 304 | sbuilder.matchEthType(Ethernet.MPLS_UNICAST); |
305 | sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId)); | 305 | sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId)); |
306 | + sbuilder.matchMplsBos(true); | ||
306 | TrafficSelector selector = sbuilder.build(); | 307 | TrafficSelector selector = sbuilder.build(); |
307 | 308 | ||
308 | // setup metadata to pass to nextObjective - indicate the vlan on egress | 309 | // setup metadata to pass to nextObjective - indicate the vlan on egress |
... | @@ -525,39 +526,6 @@ public class RoutingRulePopulator { | ... | @@ -525,39 +526,6 @@ public class RoutingRulePopulator { |
525 | } | 526 | } |
526 | 527 | ||
527 | /** | 528 | /** |
528 | - * Creates a forwarding objective to punt all IP packets, destined to the | ||
529 | - * router's port IP addresses, to the controller. Note that the input | ||
530 | - * port should not be matched on, as these packets can come from any input. | ||
531 | - * Furthermore, these are applied only by the master instance. | ||
532 | - * | ||
533 | - * @param deviceId the switch dpid for the router | ||
534 | - */ | ||
535 | - public void populateArpPunts(DeviceId deviceId) { | ||
536 | - if (!srManager.mastershipService.isLocalMaster(deviceId)) { | ||
537 | - log.debug("Not installing port-IP punts - not the master for dev:{} ", | ||
538 | - deviceId); | ||
539 | - return; | ||
540 | - } | ||
541 | - | ||
542 | - ForwardingObjective.Builder puntArp = DefaultForwardingObjective.builder(); | ||
543 | - TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); | ||
544 | - TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); | ||
545 | - sbuilder.matchEthType(Ethernet.TYPE_ARP); | ||
546 | - tbuilder.setOutput(PortNumber.CONTROLLER); | ||
547 | - puntArp.withSelector(sbuilder.build()); | ||
548 | - puntArp.withTreatment(tbuilder.build()); | ||
549 | - puntArp.withFlag(Flag.VERSATILE) | ||
550 | - .withPriority(HIGHEST_PRIORITY) | ||
551 | - .makePermanent() | ||
552 | - .fromApp(srManager.appId); | ||
553 | - log.debug("Installing forwarding objective to punt ARPs"); | ||
554 | - srManager.flowObjectiveService. | ||
555 | - forward(deviceId, | ||
556 | - puntArp.add(new SRObjectiveContext(deviceId, | ||
557 | - SRObjectiveContext.ObjectiveType.FORWARDING))); | ||
558 | - } | ||
559 | - | ||
560 | - /** | ||
561 | * Populates a forwarding objective to send packets that miss other high | 529 | * Populates a forwarding objective to send packets that miss other high |
562 | * priority Bridging Table entries to a group that contains all ports of | 530 | * priority Bridging Table entries to a group that contains all ports of |
563 | * its subnet. | 531 | * its subnet. | ... | ... |
... | @@ -51,6 +51,7 @@ import org.onosproject.net.flowobjective.ObjectiveContext; | ... | @@ -51,6 +51,7 @@ import org.onosproject.net.flowobjective.ObjectiveContext; |
51 | import org.onosproject.net.flowobjective.ObjectiveError; | 51 | import org.onosproject.net.flowobjective.ObjectiveError; |
52 | import org.onosproject.net.host.HostEvent; | 52 | import org.onosproject.net.host.HostEvent; |
53 | import org.onosproject.net.host.HostListener; | 53 | import org.onosproject.net.host.HostListener; |
54 | +import org.onosproject.net.packet.PacketPriority; | ||
54 | import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; | 55 | import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; |
55 | import org.onosproject.segmentrouting.config.DeviceConfiguration; | 56 | import org.onosproject.segmentrouting.config.DeviceConfiguration; |
56 | import org.onosproject.segmentrouting.config.SegmentRoutingConfig; | 57 | import org.onosproject.segmentrouting.config.SegmentRoutingConfig; |
... | @@ -90,6 +91,7 @@ import java.util.Collections; | ... | @@ -90,6 +91,7 @@ import java.util.Collections; |
90 | import java.util.HashSet; | 91 | import java.util.HashSet; |
91 | import java.util.List; | 92 | import java.util.List; |
92 | import java.util.Map; | 93 | import java.util.Map; |
94 | +import java.util.Optional; | ||
93 | import java.util.Set; | 95 | import java.util.Set; |
94 | import java.util.concurrent.ConcurrentHashMap; | 96 | import java.util.concurrent.ConcurrentHashMap; |
95 | import java.util.concurrent.ConcurrentLinkedQueue; | 97 | import java.util.concurrent.ConcurrentLinkedQueue; |
... | @@ -297,6 +299,11 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -297,6 +299,11 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
297 | linkService.addListener(linkListener); | 299 | linkService.addListener(linkListener); |
298 | deviceService.addListener(deviceListener); | 300 | deviceService.addListener(deviceListener); |
299 | 301 | ||
302 | + // Request ARP packet-in | ||
303 | + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); | ||
304 | + selector.matchEthType(Ethernet.TYPE_ARP); | ||
305 | + packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId, Optional.empty()); | ||
306 | + | ||
300 | cfgListener.configureNetwork(); | 307 | cfgListener.configureNetwork(); |
301 | 308 | ||
302 | log.info("Started"); | 309 | log.info("Started"); |
... | @@ -307,6 +314,11 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -307,6 +314,11 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
307 | cfgService.removeListener(cfgListener); | 314 | cfgService.removeListener(cfgListener); |
308 | cfgService.unregisterConfigFactory(cfgFactory); | 315 | cfgService.unregisterConfigFactory(cfgFactory); |
309 | 316 | ||
317 | + // Withdraw ARP packet-in | ||
318 | + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); | ||
319 | + selector.matchEthType(Ethernet.TYPE_ARP); | ||
320 | + packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId, Optional.empty()); | ||
321 | + | ||
310 | packetService.removeProcessor(processor); | 322 | packetService.removeProcessor(processor); |
311 | linkService.removeListener(linkListener); | 323 | linkService.removeListener(linkListener); |
312 | deviceService.removeListener(deviceListener); | 324 | deviceService.removeListener(deviceListener); |
... | @@ -697,7 +709,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -697,7 +709,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
697 | flowObjectiveService, | 709 | flowObjectiveService, |
698 | nsNextObjStore, | 710 | nsNextObjStore, |
699 | subnetNextObjStore, | 711 | subnetNextObjStore, |
700 | - portNextObjStore); | 712 | + portNextObjStore, |
713 | + this); | ||
701 | } catch (DeviceConfigNotFoundException e) { | 714 | } catch (DeviceConfigNotFoundException e) { |
702 | log.warn(e.getMessage() + " Aborting processDeviceAdded."); | 715 | log.warn(e.getMessage() + " Aborting processDeviceAdded."); |
703 | return; | 716 | return; |
... | @@ -766,7 +779,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -766,7 +779,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
766 | flowObjectiveService, | 779 | flowObjectiveService, |
767 | nsNextObjStore, | 780 | nsNextObjStore, |
768 | subnetNextObjStore, | 781 | subnetNextObjStore, |
769 | - portNextObjStore); | 782 | + portNextObjStore, |
783 | + segmentRoutingManager); | ||
770 | } catch (DeviceConfigNotFoundException e) { | 784 | } catch (DeviceConfigNotFoundException e) { |
771 | log.warn(e.getMessage() + " Aborting configureNetwork."); | 785 | log.warn(e.getMessage() + " Aborting configureNetwork."); |
772 | return; | 786 | return; |
... | @@ -836,23 +850,33 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -836,23 +850,33 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
836 | private ForwardingObjective.Builder getForwardingObjectiveBuilder( | 850 | private ForwardingObjective.Builder getForwardingObjectiveBuilder( |
837 | DeviceId deviceId, MacAddress mac, VlanId vlanId, | 851 | DeviceId deviceId, MacAddress mac, VlanId vlanId, |
838 | PortNumber outport) { | 852 | PortNumber outport) { |
853 | + // Get assigned VLAN for the subnet | ||
854 | + VlanId outvlan = null; | ||
855 | + Ip4Prefix subnet = deviceConfiguration.getPortSubnet(deviceId, outport); | ||
856 | + if (subnet == null) { | ||
857 | + outvlan = VlanId.vlanId(ASSIGNED_VLAN_NO_SUBNET); | ||
858 | + } else { | ||
859 | + outvlan = getSubnetAssignedVlanId(deviceId, subnet); | ||
860 | + } | ||
861 | + | ||
839 | // match rule | 862 | // match rule |
840 | TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); | 863 | TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); |
841 | sbuilder.matchEthDst(mac); | 864 | sbuilder.matchEthDst(mac); |
842 | - sbuilder.matchVlanId(vlanId); | 865 | + /* |
866 | + * Note: for untagged packets, match on the assigned VLAN. | ||
867 | + * for tagged packets, match on its incoming VLAN. | ||
868 | + */ | ||
869 | + if (vlanId.equals(VlanId.NONE)) { | ||
870 | + sbuilder.matchVlanId(outvlan); | ||
871 | + } else { | ||
872 | + sbuilder.matchVlanId(vlanId); | ||
873 | + } | ||
843 | 874 | ||
844 | TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); | 875 | TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder(); |
845 | tbuilder.immediate().popVlan(); | 876 | tbuilder.immediate().popVlan(); |
846 | tbuilder.immediate().setOutput(outport); | 877 | tbuilder.immediate().setOutput(outport); |
847 | 878 | ||
848 | // for switch pipelines that need it, provide outgoing vlan as metadata | 879 | // for switch pipelines that need it, provide outgoing vlan as metadata |
849 | - VlanId outvlan = null; | ||
850 | - Ip4Prefix subnet = deviceConfiguration.getPortSubnet(deviceId, outport); | ||
851 | - if (subnet == null) { | ||
852 | - outvlan = VlanId.vlanId(ASSIGNED_VLAN_NO_SUBNET); | ||
853 | - } else { | ||
854 | - outvlan = getSubnetAssignedVlanId(deviceId, subnet); | ||
855 | - } | ||
856 | TrafficSelector meta = DefaultTrafficSelector.builder() | 880 | TrafficSelector meta = DefaultTrafficSelector.builder() |
857 | .matchVlanId(outvlan).build(); | 881 | .matchVlanId(outvlan).build(); |
858 | 882 | ... | ... |
... | @@ -24,6 +24,7 @@ import org.onosproject.net.DeviceId; | ... | @@ -24,6 +24,7 @@ import org.onosproject.net.DeviceId; |
24 | import org.onosproject.net.Link; | 24 | import org.onosproject.net.Link; |
25 | import org.onosproject.net.flowobjective.FlowObjectiveService; | 25 | import org.onosproject.net.flowobjective.FlowObjectiveService; |
26 | import org.onosproject.net.link.LinkService; | 26 | import org.onosproject.net.link.LinkService; |
27 | +import org.onosproject.segmentrouting.SegmentRoutingManager; | ||
27 | import org.onosproject.segmentrouting.config.DeviceProperties; | 28 | import org.onosproject.segmentrouting.config.DeviceProperties; |
28 | import org.onosproject.store.service.EventuallyConsistentMap; | 29 | import org.onosproject.store.service.EventuallyConsistentMap; |
29 | 30 | ||
... | @@ -46,7 +47,7 @@ import org.onosproject.store.service.EventuallyConsistentMap; | ... | @@ -46,7 +47,7 @@ import org.onosproject.store.service.EventuallyConsistentMap; |
46 | * 8) what about ecmp no label case | 47 | * 8) what about ecmp no label case |
47 | */ | 48 | */ |
48 | public class DefaultEdgeGroupHandler extends DefaultGroupHandler { | 49 | public class DefaultEdgeGroupHandler extends DefaultGroupHandler { |
49 | - | 50 | + // TODO Access stores through srManager |
50 | protected DefaultEdgeGroupHandler(DeviceId deviceId, | 51 | protected DefaultEdgeGroupHandler(DeviceId deviceId, |
51 | ApplicationId appId, | 52 | ApplicationId appId, |
52 | DeviceProperties config, | 53 | DeviceProperties config, |
... | @@ -58,9 +59,10 @@ public class DefaultEdgeGroupHandler extends DefaultGroupHandler { | ... | @@ -58,9 +59,10 @@ public class DefaultEdgeGroupHandler extends DefaultGroupHandler { |
58 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, | 59 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, |
59 | Integer> subnetNextObjStore, | 60 | Integer> subnetNextObjStore, |
60 | EventuallyConsistentMap<PortNextObjectiveStoreKey, | 61 | EventuallyConsistentMap<PortNextObjectiveStoreKey, |
61 | - Integer> portNextObjStore) { | 62 | + Integer> portNextObjStore, |
63 | + SegmentRoutingManager srManager) { | ||
62 | super(deviceId, appId, config, linkService, flowObjService, | 64 | super(deviceId, appId, config, linkService, flowObjService, |
63 | - nsNextObjStore, subnetNextObjStore, portNextObjStore); | 65 | + nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager); |
64 | } | 66 | } |
65 | 67 | ||
66 | @Override | 68 | @Override | ... | ... |
... | @@ -87,6 +87,7 @@ public class DefaultGroupHandler { | ... | @@ -87,6 +87,7 @@ public class DefaultGroupHandler { |
87 | SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null; | 87 | SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null; |
88 | protected EventuallyConsistentMap< | 88 | protected EventuallyConsistentMap< |
89 | PortNextObjectiveStoreKey, Integer> portNextObjStore = null; | 89 | PortNextObjectiveStoreKey, Integer> portNextObjStore = null; |
90 | + private SegmentRoutingManager srManager; | ||
90 | 91 | ||
91 | protected KryoNamespace.Builder kryo = new KryoNamespace.Builder() | 92 | protected KryoNamespace.Builder kryo = new KryoNamespace.Builder() |
92 | .register(URI.class).register(HashSet.class) | 93 | .register(URI.class).register(HashSet.class) |
... | @@ -96,6 +97,7 @@ public class DefaultGroupHandler { | ... | @@ -96,6 +97,7 @@ public class DefaultGroupHandler { |
96 | .register(GroupBucketIdentifier.class) | 97 | .register(GroupBucketIdentifier.class) |
97 | .register(GroupBucketIdentifier.BucketOutputType.class); | 98 | .register(GroupBucketIdentifier.BucketOutputType.class); |
98 | 99 | ||
100 | + // TODO Access stores through srManager | ||
99 | protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId, | 101 | protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId, |
100 | DeviceProperties config, | 102 | DeviceProperties config, |
101 | LinkService linkService, | 103 | LinkService linkService, |
... | @@ -105,7 +107,8 @@ public class DefaultGroupHandler { | ... | @@ -105,7 +107,8 @@ public class DefaultGroupHandler { |
105 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, | 107 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, |
106 | Integer> subnetNextObjStore, | 108 | Integer> subnetNextObjStore, |
107 | EventuallyConsistentMap<PortNextObjectiveStoreKey, | 109 | EventuallyConsistentMap<PortNextObjectiveStoreKey, |
108 | - Integer> portNextObjStore) { | 110 | + Integer> portNextObjStore, |
111 | + SegmentRoutingManager srManager) { | ||
109 | this.deviceId = checkNotNull(deviceId); | 112 | this.deviceId = checkNotNull(deviceId); |
110 | this.appId = checkNotNull(appId); | 113 | this.appId = checkNotNull(appId); |
111 | this.deviceConfig = checkNotNull(config); | 114 | this.deviceConfig = checkNotNull(config); |
... | @@ -123,6 +126,7 @@ public class DefaultGroupHandler { | ... | @@ -123,6 +126,7 @@ public class DefaultGroupHandler { |
123 | this.nsNextObjStore = nsNextObjStore; | 126 | this.nsNextObjStore = nsNextObjStore; |
124 | this.subnetNextObjStore = subnetNextObjStore; | 127 | this.subnetNextObjStore = subnetNextObjStore; |
125 | this.portNextObjStore = portNextObjStore; | 128 | this.portNextObjStore = portNextObjStore; |
129 | + this.srManager = srManager; | ||
126 | 130 | ||
127 | populateNeighborMaps(); | 131 | populateNeighborMaps(); |
128 | } | 132 | } |
... | @@ -153,7 +157,8 @@ public class DefaultGroupHandler { | ... | @@ -153,7 +157,8 @@ public class DefaultGroupHandler { |
153 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, | 157 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, |
154 | Integer> subnetNextObjStore, | 158 | Integer> subnetNextObjStore, |
155 | EventuallyConsistentMap<PortNextObjectiveStoreKey, | 159 | EventuallyConsistentMap<PortNextObjectiveStoreKey, |
156 | - Integer> portNextObjStore) | 160 | + Integer> portNextObjStore, |
161 | + SegmentRoutingManager srManager) | ||
157 | throws DeviceConfigNotFoundException { | 162 | throws DeviceConfigNotFoundException { |
158 | // handle possible exception in the caller | 163 | // handle possible exception in the caller |
159 | if (config.isEdgeDevice(deviceId)) { | 164 | if (config.isEdgeDevice(deviceId)) { |
... | @@ -162,14 +167,17 @@ public class DefaultGroupHandler { | ... | @@ -162,14 +167,17 @@ public class DefaultGroupHandler { |
162 | flowObjService, | 167 | flowObjService, |
163 | nsNextObjStore, | 168 | nsNextObjStore, |
164 | subnetNextObjStore, | 169 | subnetNextObjStore, |
165 | - portNextObjStore); | 170 | + portNextObjStore, |
171 | + srManager | ||
172 | + ); | ||
166 | } else { | 173 | } else { |
167 | return new DefaultTransitGroupHandler(deviceId, appId, config, | 174 | return new DefaultTransitGroupHandler(deviceId, appId, config, |
168 | linkService, | 175 | linkService, |
169 | flowObjService, | 176 | flowObjService, |
170 | nsNextObjStore, | 177 | nsNextObjStore, |
171 | subnetNextObjStore, | 178 | subnetNextObjStore, |
172 | - portNextObjStore); | 179 | + portNextObjStore, |
180 | + srManager); | ||
173 | } | 181 | } |
174 | } | 182 | } |
175 | 183 | ||
... | @@ -663,11 +671,17 @@ public class DefaultGroupHandler { | ... | @@ -663,11 +671,17 @@ public class DefaultGroupHandler { |
663 | return; | 671 | return; |
664 | } | 672 | } |
665 | 673 | ||
674 | + VlanId assignedVlanId = | ||
675 | + srManager.getSubnetAssignedVlanId(this.deviceId, subnet); | ||
676 | + TrafficSelector metadata = | ||
677 | + DefaultTrafficSelector.builder().matchVlanId(assignedVlanId).build(); | ||
678 | + | ||
666 | int nextId = flowObjectiveService.allocateNextId(); | 679 | int nextId = flowObjectiveService.allocateNextId(); |
667 | 680 | ||
668 | NextObjective.Builder nextObjBuilder = DefaultNextObjective | 681 | NextObjective.Builder nextObjBuilder = DefaultNextObjective |
669 | .builder().withId(nextId) | 682 | .builder().withId(nextId) |
670 | - .withType(NextObjective.Type.BROADCAST).fromApp(appId); | 683 | + .withType(NextObjective.Type.BROADCAST).fromApp(appId) |
684 | + .withMeta(metadata); | ||
671 | 685 | ||
672 | ports.forEach(port -> { | 686 | ports.forEach(port -> { |
673 | TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); | 687 | TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); | ... | ... |
... | @@ -23,6 +23,7 @@ import org.onosproject.net.DeviceId; | ... | @@ -23,6 +23,7 @@ import org.onosproject.net.DeviceId; |
23 | import org.onosproject.net.Link; | 23 | import org.onosproject.net.Link; |
24 | import org.onosproject.net.flowobjective.FlowObjectiveService; | 24 | import org.onosproject.net.flowobjective.FlowObjectiveService; |
25 | import org.onosproject.net.link.LinkService; | 25 | import org.onosproject.net.link.LinkService; |
26 | +import org.onosproject.segmentrouting.SegmentRoutingManager; | ||
26 | import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; | 27 | import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; |
27 | import org.onosproject.segmentrouting.config.DeviceProperties; | 28 | import org.onosproject.segmentrouting.config.DeviceProperties; |
28 | import org.onosproject.store.service.EventuallyConsistentMap; | 29 | import org.onosproject.store.service.EventuallyConsistentMap; |
... | @@ -40,7 +41,7 @@ import org.onosproject.store.service.EventuallyConsistentMap; | ... | @@ -40,7 +41,7 @@ import org.onosproject.store.service.EventuallyConsistentMap; |
40 | * 2) all ports to D3 + with no label push, | 41 | * 2) all ports to D3 + with no label push, |
41 | */ | 42 | */ |
42 | public class DefaultTransitGroupHandler extends DefaultGroupHandler { | 43 | public class DefaultTransitGroupHandler extends DefaultGroupHandler { |
43 | - | 44 | + // TODO Access stores through srManager |
44 | protected DefaultTransitGroupHandler(DeviceId deviceId, | 45 | protected DefaultTransitGroupHandler(DeviceId deviceId, |
45 | ApplicationId appId, | 46 | ApplicationId appId, |
46 | DeviceProperties config, | 47 | DeviceProperties config, |
... | @@ -52,9 +53,10 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler { | ... | @@ -52,9 +53,10 @@ public class DefaultTransitGroupHandler extends DefaultGroupHandler { |
52 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, | 53 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, |
53 | Integer> subnetNextObjStore, | 54 | Integer> subnetNextObjStore, |
54 | EventuallyConsistentMap<PortNextObjectiveStoreKey, | 55 | EventuallyConsistentMap<PortNextObjectiveStoreKey, |
55 | - Integer> portNextObjStore) { | 56 | + Integer> portNextObjStore, |
57 | + SegmentRoutingManager srManager) { | ||
56 | super(deviceId, appId, config, linkService, flowObjService, | 58 | super(deviceId, appId, config, linkService, flowObjService, |
57 | - nsNextObjStore, subnetNextObjStore, portNextObjStore); | 59 | + nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager); |
58 | } | 60 | } |
59 | 61 | ||
60 | @Override | 62 | @Override | ... | ... |
... | @@ -27,6 +27,7 @@ import java.util.List; | ... | @@ -27,6 +27,7 @@ import java.util.List; |
27 | import org.onlab.packet.MacAddress; | 27 | import org.onlab.packet.MacAddress; |
28 | import org.onlab.packet.MplsLabel; | 28 | import org.onlab.packet.MplsLabel; |
29 | import org.onosproject.core.ApplicationId; | 29 | import org.onosproject.core.ApplicationId; |
30 | +import org.onosproject.segmentrouting.SegmentRoutingManager; | ||
30 | import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; | 31 | import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; |
31 | import org.onosproject.segmentrouting.config.DeviceProperties; | 32 | import org.onosproject.segmentrouting.config.DeviceProperties; |
32 | import org.onosproject.segmentrouting.grouphandler.GroupBucketIdentifier.BucketOutputType; | 33 | import org.onosproject.segmentrouting.grouphandler.GroupBucketIdentifier.BucketOutputType; |
... | @@ -60,6 +61,7 @@ public class PolicyGroupHandler extends DefaultGroupHandler { | ... | @@ -60,6 +61,7 @@ public class PolicyGroupHandler extends DefaultGroupHandler { |
60 | * @param nsNextObjStore NeighborSet next objective store map | 61 | * @param nsNextObjStore NeighborSet next objective store map |
61 | * @param subnetNextObjStore subnet next objective store map | 62 | * @param subnetNextObjStore subnet next objective store map |
62 | */ | 63 | */ |
64 | + // TODO Access stores through srManager | ||
63 | public PolicyGroupHandler(DeviceId deviceId, | 65 | public PolicyGroupHandler(DeviceId deviceId, |
64 | ApplicationId appId, | 66 | ApplicationId appId, |
65 | DeviceProperties config, | 67 | DeviceProperties config, |
... | @@ -70,9 +72,10 @@ public class PolicyGroupHandler extends DefaultGroupHandler { | ... | @@ -70,9 +72,10 @@ public class PolicyGroupHandler extends DefaultGroupHandler { |
70 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, | 72 | EventuallyConsistentMap<SubnetNextObjectiveStoreKey, |
71 | Integer> subnetNextObjStore, | 73 | Integer> subnetNextObjStore, |
72 | EventuallyConsistentMap<PortNextObjectiveStoreKey, | 74 | EventuallyConsistentMap<PortNextObjectiveStoreKey, |
73 | - Integer> portNextObjStore) { | 75 | + Integer> portNextObjStore, |
76 | + SegmentRoutingManager srManager) { | ||
74 | super(deviceId, appId, config, linkService, flowObjService, | 77 | super(deviceId, appId, config, linkService, flowObjService, |
75 | - nsNextObjStore, subnetNextObjStore, portNextObjStore); | 78 | + nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager); |
76 | } | 79 | } |
77 | 80 | ||
78 | public PolicyGroupIdentifier createPolicyGroupChain(String id, | 81 | public PolicyGroupIdentifier createPolicyGroupChain(String id, | ... | ... |
... | @@ -106,13 +106,13 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline { | ... | @@ -106,13 +106,13 @@ public class CpqdOFDPA2Pipeline extends OFDPA2Pipeline { |
106 | 106 | ||
107 | for (PortNumber pnum : portnums) { | 107 | for (PortNumber pnum : portnums) { |
108 | // update storage | 108 | // update storage |
109 | - port2Vlan.put(pnum, storeVlan); | 109 | + ofdpa2GroupHandler.port2Vlan.put(pnum, storeVlan); |
110 | - Set<PortNumber> vlanPorts = vlan2Port.get(storeVlan); | 110 | + Set<PortNumber> vlanPorts = ofdpa2GroupHandler.vlan2Port.get(storeVlan); |
111 | if (vlanPorts == null) { | 111 | if (vlanPorts == null) { |
112 | vlanPorts = Collections.newSetFromMap( | 112 | vlanPorts = Collections.newSetFromMap( |
113 | new ConcurrentHashMap<PortNumber, Boolean>()); | 113 | new ConcurrentHashMap<PortNumber, Boolean>()); |
114 | vlanPorts.add(pnum); | 114 | vlanPorts.add(pnum); |
115 | - vlan2Port.put(storeVlan, vlanPorts); | 115 | + ofdpa2GroupHandler.vlan2Port.put(storeVlan, vlanPorts); |
116 | } else { | 116 | } else { |
117 | vlanPorts.add(pnum); | 117 | vlanPorts.add(pnum); |
118 | } | 118 | } | ... | ... |
1 | +package org.onosproject.driver.pipeline; | ||
2 | + | ||
3 | +import com.google.common.cache.Cache; | ||
4 | +import com.google.common.cache.CacheBuilder; | ||
5 | +import com.google.common.cache.RemovalCause; | ||
6 | +import com.google.common.cache.RemovalNotification; | ||
7 | +import org.onlab.osgi.ServiceDirectory; | ||
8 | +import org.onlab.packet.MplsLabel; | ||
9 | +import org.onlab.packet.VlanId; | ||
10 | +import org.onosproject.core.ApplicationId; | ||
11 | +import org.onosproject.core.DefaultGroupId; | ||
12 | +import org.onosproject.net.DeviceId; | ||
13 | +import org.onosproject.net.PortNumber; | ||
14 | +import org.onosproject.net.behaviour.NextGroup; | ||
15 | +import org.onosproject.net.behaviour.PipelinerContext; | ||
16 | +import org.onosproject.net.flow.DefaultTrafficTreatment; | ||
17 | +import org.onosproject.net.flow.TrafficSelector; | ||
18 | +import org.onosproject.net.flow.TrafficTreatment; | ||
19 | +import org.onosproject.net.flow.criteria.Criterion; | ||
20 | +import org.onosproject.net.flow.criteria.VlanIdCriterion; | ||
21 | +import org.onosproject.net.flow.instructions.Instruction; | ||
22 | +import org.onosproject.net.flow.instructions.Instructions; | ||
23 | +import org.onosproject.net.flow.instructions.L2ModificationInstruction; | ||
24 | +import org.onosproject.net.flowobjective.FlowObjectiveStore; | ||
25 | +import org.onosproject.net.flowobjective.NextObjective; | ||
26 | +import org.onosproject.net.flowobjective.ObjectiveError; | ||
27 | +import org.onosproject.net.group.DefaultGroupBucket; | ||
28 | +import org.onosproject.net.group.DefaultGroupDescription; | ||
29 | +import org.onosproject.net.group.DefaultGroupKey; | ||
30 | +import org.onosproject.net.group.Group; | ||
31 | +import org.onosproject.net.group.GroupBucket; | ||
32 | +import org.onosproject.net.group.GroupBuckets; | ||
33 | +import org.onosproject.net.group.GroupDescription; | ||
34 | +import org.onosproject.net.group.GroupEvent; | ||
35 | +import org.onosproject.net.group.GroupKey; | ||
36 | +import org.onosproject.net.group.GroupListener; | ||
37 | +import org.onosproject.net.group.GroupService; | ||
38 | +import org.slf4j.Logger; | ||
39 | + | ||
40 | +import java.util.ArrayDeque; | ||
41 | +import java.util.ArrayList; | ||
42 | +import java.util.Collection; | ||
43 | +import java.util.Collections; | ||
44 | +import java.util.Deque; | ||
45 | +import java.util.List; | ||
46 | +import java.util.Map; | ||
47 | +import java.util.Set; | ||
48 | +import java.util.concurrent.ConcurrentHashMap; | ||
49 | +import java.util.concurrent.CopyOnWriteArrayList; | ||
50 | +import java.util.concurrent.Executors; | ||
51 | +import java.util.concurrent.ScheduledExecutorService; | ||
52 | +import java.util.concurrent.TimeUnit; | ||
53 | +import java.util.concurrent.atomic.AtomicInteger; | ||
54 | +import java.util.stream.Collectors; | ||
55 | + | ||
56 | +import static org.onlab.util.Tools.groupedThreads; | ||
57 | +import static org.slf4j.LoggerFactory.getLogger; | ||
58 | + | ||
59 | +/** | ||
60 | + * Group handler for OFDPA2 pipeline. | ||
61 | + */ | ||
62 | +public class OFDPA2GroupHandler { | ||
63 | + /* | ||
64 | + * OFDPA requires group-id's to have a certain form. | ||
65 | + * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid> | ||
66 | + * L3 Unicast Groups have <4bits-2><28bits-index> | ||
67 | + * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index> | ||
68 | + * L3 ECMP Groups have <4bits-7><28bits-index> | ||
69 | + * L2 Flood Groups have <4bits-4><12bits-vlanid><16bits-index> | ||
70 | + * L3 VPN Groups have <4bits-9><4bits-2><24bits-index> | ||
71 | + */ | ||
72 | + private static final int L2INTERFACEMASK = 0x0; | ||
73 | + private static final int L3UNICASTMASK = 0x20000000; | ||
74 | + private static final int MPLSINTERFACEMASK = 0x90000000; | ||
75 | + private static final int L3ECMPMASK = 0x70000000; | ||
76 | + private static final int L2FLOODMASK = 0x40000000; | ||
77 | + private static final int L3VPNMASK = 0x92000000; | ||
78 | + | ||
79 | + private final Logger log = getLogger(getClass()); | ||
80 | + private ServiceDirectory serviceDirectory; | ||
81 | + protected GroupService groupService; | ||
82 | + | ||
83 | + private DeviceId deviceId; | ||
84 | + private FlowObjectiveStore flowObjectiveStore; | ||
85 | + private Cache<GroupKey, List<OfdpaNextGroup>> pendingNextObjectives; | ||
86 | + private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups; | ||
87 | + private ScheduledExecutorService groupChecker = | ||
88 | + Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner", "ofdpa2-%d")); | ||
89 | + | ||
90 | + // index number for group creation | ||
91 | + private AtomicInteger l3vpnindex = new AtomicInteger(0); | ||
92 | + | ||
93 | + // local stores for port-vlan mapping | ||
94 | + protected Map<PortNumber, VlanId> port2Vlan = new ConcurrentHashMap<>(); | ||
95 | + protected Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<>(); | ||
96 | + | ||
97 | + // local store for pending bucketAdds - by design there can only be one | ||
98 | + // pending bucket for a group | ||
99 | + protected ConcurrentHashMap<Integer, NextObjective> pendingBuckets = new ConcurrentHashMap<>(); | ||
100 | + | ||
101 | + protected void init(DeviceId deviceId, PipelinerContext context) { | ||
102 | + this.deviceId = deviceId; | ||
103 | + this.flowObjectiveStore = context.store(); | ||
104 | + this.serviceDirectory = context.directory(); | ||
105 | + this.groupService = serviceDirectory.get(GroupService.class); | ||
106 | + | ||
107 | + pendingNextObjectives = CacheBuilder.newBuilder() | ||
108 | + .expireAfterWrite(20, TimeUnit.SECONDS) | ||
109 | + .removalListener(( | ||
110 | + RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> { | ||
111 | + if (notification.getCause() == RemovalCause.EXPIRED) { | ||
112 | + notification.getValue().forEach(ofdpaNextGrp -> | ||
113 | + OFDPA2Pipeline.fail(ofdpaNextGrp.nextObj, | ||
114 | + ObjectiveError.GROUPINSTALLATIONFAILED)); | ||
115 | + | ||
116 | + } | ||
117 | + }).build(); | ||
118 | + pendingGroups = new ConcurrentHashMap<>(); | ||
119 | + groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS); | ||
120 | + | ||
121 | + groupService.addListener(new InnerGroupListener()); | ||
122 | + } | ||
123 | + | ||
124 | + protected void addGroup(NextObjective nextObjective) { | ||
125 | + switch (nextObjective.type()) { | ||
126 | + case SIMPLE: | ||
127 | + Collection<TrafficTreatment> treatments = nextObjective.next(); | ||
128 | + if (treatments.size() != 1) { | ||
129 | + log.error("Next Objectives of type Simple should only have a " | ||
130 | + + "single Traffic Treatment. Next Objective Id:{}", | ||
131 | + nextObjective.id()); | ||
132 | + OFDPA2Pipeline.fail(nextObjective, ObjectiveError.BADPARAMS); | ||
133 | + return; | ||
134 | + } | ||
135 | + processSimpleNextObjective(nextObjective); | ||
136 | + break; | ||
137 | + case BROADCAST: | ||
138 | + processBroadcastNextObjective(nextObjective); | ||
139 | + break; | ||
140 | + case HASHED: | ||
141 | + processHashedNextObjective(nextObjective); | ||
142 | + break; | ||
143 | + case FAILOVER: | ||
144 | + OFDPA2Pipeline.fail(nextObjective, ObjectiveError.UNSUPPORTED); | ||
145 | + log.warn("Unsupported next objective type {}", nextObjective.type()); | ||
146 | + break; | ||
147 | + default: | ||
148 | + OFDPA2Pipeline.fail(nextObjective, ObjectiveError.UNKNOWN); | ||
149 | + log.warn("Unknown next objective type {}", nextObjective.type()); | ||
150 | + } | ||
151 | + } | ||
152 | + | ||
153 | + /** | ||
154 | + * As per the OFDPA 2.0 TTP, packets are sent out of ports by using | ||
155 | + * a chain of groups. The simple Next Objective passed | ||
156 | + * in by the application has to be broken up into a group chain | ||
157 | + * comprising of an L3 Unicast Group that points to an L2 Interface | ||
158 | + * Group which in-turn points to an output port. In some cases, the simple | ||
159 | + * next Objective can just be an L2 interface without the need for chaining. | ||
160 | + * | ||
161 | + * @param nextObj the nextObjective of type SIMPLE | ||
162 | + */ | ||
163 | + private void processSimpleNextObjective(NextObjective nextObj) { | ||
164 | + TrafficTreatment treatment = nextObj.next().iterator().next(); | ||
165 | + // determine if plain L2 or L3->L2 | ||
166 | + boolean plainL2 = true; | ||
167 | + for (Instruction ins : treatment.allInstructions()) { | ||
168 | + if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
169 | + L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
170 | + if (l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_DST || | ||
171 | + l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_SRC) { | ||
172 | + plainL2 = false; | ||
173 | + break; | ||
174 | + } | ||
175 | + } | ||
176 | + } | ||
177 | + | ||
178 | + if (plainL2) { | ||
179 | + createL2InterfaceGroup(nextObj); | ||
180 | + return; | ||
181 | + } | ||
182 | + | ||
183 | + // break up simple next objective to GroupChain objects | ||
184 | + GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(), | ||
185 | + nextObj.appId(), false, | ||
186 | + nextObj.meta()); | ||
187 | + if (groupInfo == null) { | ||
188 | + log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId); | ||
189 | + return; | ||
190 | + } | ||
191 | + // create object for local and distributed storage | ||
192 | + Deque<GroupKey> gkeyChain = new ArrayDeque<>(); | ||
193 | + gkeyChain.addFirst(groupInfo.innerGrpDesc.appCookie()); | ||
194 | + gkeyChain.addFirst(groupInfo.outerGrpDesc.appCookie()); | ||
195 | + OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup( | ||
196 | + Collections.singletonList(gkeyChain), | ||
197 | + nextObj); | ||
198 | + | ||
199 | + // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it | ||
200 | + updatePendingNextObjective(groupInfo.outerGrpDesc.appCookie(), ofdpaGrp); | ||
201 | + | ||
202 | + // now we are ready to send the l2 groupDescription (inner), as all the stores | ||
203 | + // that will get async replies have been updated. By waiting to update | ||
204 | + // the stores, we prevent nasty race conditions. | ||
205 | + groupService.addGroup(groupInfo.innerGrpDesc); | ||
206 | + } | ||
207 | + | ||
208 | + private void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) { | ||
209 | + List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>(); | ||
210 | + nextList.add(value); | ||
211 | + List<OfdpaNextGroup> ret = pendingNextObjectives.asMap() | ||
212 | + .putIfAbsent(key, nextList); | ||
213 | + if (ret != null) { | ||
214 | + ret.add(value); | ||
215 | + } | ||
216 | + } | ||
217 | + | ||
218 | + private void updatePendingGroups(GroupKey gkey, GroupChainElem gce) { | ||
219 | + Set<GroupChainElem> gceSet = Collections.newSetFromMap( | ||
220 | + new ConcurrentHashMap<GroupChainElem, Boolean>()); | ||
221 | + gceSet.add(gce); | ||
222 | + Set<GroupChainElem> retval = pendingGroups.putIfAbsent(gkey, gceSet); | ||
223 | + if (retval != null) { | ||
224 | + retval.add(gce); | ||
225 | + } | ||
226 | + } | ||
227 | + | ||
228 | + /** | ||
229 | + * Creates a simple L2 Interface Group. | ||
230 | + * | ||
231 | + * @param nextObj the next Objective | ||
232 | + */ | ||
233 | + private void createL2InterfaceGroup(NextObjective nextObj) { | ||
234 | + // only allowed actions are vlan pop and outport | ||
235 | + TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); | ||
236 | + PortNumber portNum = null; | ||
237 | + for (Instruction ins : nextObj.next().iterator().next().allInstructions()) { | ||
238 | + if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
239 | + L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
240 | + switch (l2ins.subtype()) { | ||
241 | + case VLAN_POP: | ||
242 | + ttb.add(l2ins); | ||
243 | + break; | ||
244 | + default: | ||
245 | + break; | ||
246 | + } | ||
247 | + } else if (ins.type() == Instruction.Type.OUTPUT) { | ||
248 | + portNum = ((Instructions.OutputInstruction) ins).port(); | ||
249 | + ttb.add(ins); | ||
250 | + } else { | ||
251 | + log.warn("Driver does not handle this type of TrafficTreatment" | ||
252 | + + " instruction in simple nextObjectives: {}", ins.type()); | ||
253 | + } | ||
254 | + } | ||
255 | + //use the vlanid associated with the port | ||
256 | + VlanId vlanid = port2Vlan.get(portNum); | ||
257 | + | ||
258 | + if (vlanid == null && nextObj.meta() != null) { | ||
259 | + // use metadata vlan info if available | ||
260 | + Criterion vidCriterion = nextObj.meta().getCriterion(Criterion.Type.VLAN_VID); | ||
261 | + if (vidCriterion != null) { | ||
262 | + vlanid = ((VlanIdCriterion) vidCriterion).vlanId(); | ||
263 | + } | ||
264 | + } | ||
265 | + | ||
266 | + if (vlanid == null) { | ||
267 | + log.error("Driver cannot process an L2/L3 group chain without " | ||
268 | + + "egress vlan information for dev: {} port:{}", | ||
269 | + deviceId, portNum); | ||
270 | + return; | ||
271 | + } | ||
272 | + | ||
273 | + // assemble information for ofdpa l2interface group | ||
274 | + Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum.toLong(); | ||
275 | + // a globally unique groupkey that is different for ports in the same devices | ||
276 | + // but different for the same portnumber on different devices. Also different | ||
277 | + // for the various group-types created out of the same next objective. | ||
278 | + int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong()); | ||
279 | + final GroupKey l2groupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l2gk)); | ||
280 | + | ||
281 | + // create group description for the l2interfacegroup | ||
282 | + GroupBucket l2interfaceGroupBucket = | ||
283 | + DefaultGroupBucket.createIndirectGroupBucket(ttb.build()); | ||
284 | + GroupDescription l2groupDescription = | ||
285 | + new DefaultGroupDescription( | ||
286 | + deviceId, | ||
287 | + GroupDescription.Type.INDIRECT, | ||
288 | + new GroupBuckets(Collections.singletonList( | ||
289 | + l2interfaceGroupBucket)), | ||
290 | + l2groupkey, | ||
291 | + l2groupId, | ||
292 | + nextObj.appId()); | ||
293 | + log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}", | ||
294 | + deviceId, Integer.toHexString(l2groupId), | ||
295 | + l2groupkey, nextObj.id()); | ||
296 | + | ||
297 | + // create object for local and distributed storage | ||
298 | + Deque<GroupKey> singleKey = new ArrayDeque<>(); | ||
299 | + singleKey.addFirst(l2groupkey); | ||
300 | + OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup( | ||
301 | + Collections.singletonList(singleKey), | ||
302 | + nextObj); | ||
303 | + | ||
304 | + // store l2groupkey for the nextObjective that depends on it | ||
305 | + updatePendingNextObjective(l2groupkey, ofdpaGrp); | ||
306 | + // send the group description to the group service | ||
307 | + groupService.addGroup(l2groupDescription); | ||
308 | + } | ||
309 | + | ||
310 | + /** | ||
311 | + * Creates one of two possible group-chains from the treatment | ||
312 | + * passed in. Depending on the MPLS boolean, this method either creates | ||
313 | + * an L3Unicast Group --> L2Interface Group, if mpls is false; | ||
314 | + * or MPLSInterface Group --> L2Interface Group, if mpls is true; | ||
315 | + * The returned 'inner' group description is always the L2 Interface group. | ||
316 | + * | ||
317 | + * @param treatment that needs to be broken up to create the group chain | ||
318 | + * @param nextId of the next objective that needs this group chain | ||
319 | + * @param appId of the application that sent this next objective | ||
320 | + * @param mpls determines if L3Unicast or MPLSInterface group is created | ||
321 | + * @param meta metadata passed in by the application as part of the nextObjective | ||
322 | + * @return GroupInfo containing the GroupDescription of the | ||
323 | + * L2Interface group(inner) and the GroupDescription of the (outer) | ||
324 | + * L3Unicast/MPLSInterface group. May return null if there is an | ||
325 | + * error in processing the chain | ||
326 | + */ | ||
327 | + private GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId, | ||
328 | + ApplicationId appId, boolean mpls, | ||
329 | + TrafficSelector meta) { | ||
330 | + // for the l2interface group, get vlan and port info | ||
331 | + // for the outer group, get the src/dst mac, and vlan info | ||
332 | + TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder(); | ||
333 | + TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder(); | ||
334 | + VlanId vlanid = null; | ||
335 | + long portNum = 0; | ||
336 | + boolean setVlan = false, popVlan = false; | ||
337 | + for (Instruction ins : treatment.allInstructions()) { | ||
338 | + if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
339 | + L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
340 | + switch (l2ins.subtype()) { | ||
341 | + case ETH_DST: | ||
342 | + outerTtb.setEthDst(((L2ModificationInstruction.ModEtherInstruction) l2ins).mac()); | ||
343 | + break; | ||
344 | + case ETH_SRC: | ||
345 | + outerTtb.setEthSrc(((L2ModificationInstruction.ModEtherInstruction) l2ins).mac()); | ||
346 | + break; | ||
347 | + case VLAN_ID: | ||
348 | + vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId(); | ||
349 | + outerTtb.setVlanId(vlanid); | ||
350 | + setVlan = true; | ||
351 | + break; | ||
352 | + case VLAN_POP: | ||
353 | + innerTtb.popVlan(); | ||
354 | + popVlan = true; | ||
355 | + break; | ||
356 | + case DEC_MPLS_TTL: | ||
357 | + case MPLS_LABEL: | ||
358 | + case MPLS_POP: | ||
359 | + case MPLS_PUSH: | ||
360 | + case VLAN_PCP: | ||
361 | + case VLAN_PUSH: | ||
362 | + default: | ||
363 | + break; | ||
364 | + } | ||
365 | + } else if (ins.type() == Instruction.Type.OUTPUT) { | ||
366 | + portNum = ((Instructions.OutputInstruction) ins).port().toLong(); | ||
367 | + innerTtb.add(ins); | ||
368 | + } else { | ||
369 | + log.warn("Driver does not handle this type of TrafficTreatment" | ||
370 | + + " instruction in nextObjectives: {}", ins.type()); | ||
371 | + } | ||
372 | + } | ||
373 | + | ||
374 | + if (vlanid == null && meta != null) { | ||
375 | + // use metadata if available | ||
376 | + Criterion vidCriterion = meta.getCriterion(Criterion.Type.VLAN_VID); | ||
377 | + if (vidCriterion != null) { | ||
378 | + vlanid = ((VlanIdCriterion) vidCriterion).vlanId(); | ||
379 | + } | ||
380 | + // if vlan is not set, use the vlan in metadata for outerTtb | ||
381 | + if (vlanid != null && !setVlan) { | ||
382 | + outerTtb.setVlanId(vlanid); | ||
383 | + } | ||
384 | + } | ||
385 | + | ||
386 | + if (vlanid == null) { | ||
387 | + log.error("Driver cannot process an L2/L3 group chain without " | ||
388 | + + "egress vlan information for dev: {} port:{}", | ||
389 | + deviceId, portNum); | ||
390 | + return null; | ||
391 | + } | ||
392 | + | ||
393 | + if (!setVlan && !popVlan) { | ||
394 | + // untagged outgoing port | ||
395 | + TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder(); | ||
396 | + temp.popVlan(); | ||
397 | + innerTtb.build().allInstructions().forEach(i -> temp.add(i)); | ||
398 | + innerTtb = temp; | ||
399 | + } | ||
400 | + | ||
401 | + // assemble information for ofdpa l2interface group | ||
402 | + Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum; | ||
403 | + // a globally unique groupkey that is different for ports in the same devices | ||
404 | + // but different for the same portnumber on different devices. Also different | ||
405 | + // for the various group-types created out of the same next objective. | ||
406 | + int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum); | ||
407 | + final GroupKey l2groupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l2gk)); | ||
408 | + | ||
409 | + // assemble information for outer group | ||
410 | + GroupDescription outerGrpDesc = null; | ||
411 | + if (mpls) { | ||
412 | + // outer group is MPLSInteface | ||
413 | + Integer mplsgroupId = MPLSINTERFACEMASK | (int) portNum; | ||
414 | + // using mplsinterfacemask in groupkey to differentiate from l2interface | ||
415 | + int mplsgk = MPLSINTERFACEMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum)); | ||
416 | + final GroupKey mplsgroupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(mplsgk)); | ||
417 | + outerTtb.group(new DefaultGroupId(l2groupId)); | ||
418 | + // create the mpls-interface group description to wait for the | ||
419 | + // l2 interface group to be processed | ||
420 | + GroupBucket mplsinterfaceGroupBucket = | ||
421 | + DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build()); | ||
422 | + outerGrpDesc = new DefaultGroupDescription( | ||
423 | + deviceId, | ||
424 | + GroupDescription.Type.INDIRECT, | ||
425 | + new GroupBuckets(Collections.singletonList( | ||
426 | + mplsinterfaceGroupBucket)), | ||
427 | + mplsgroupkey, | ||
428 | + mplsgroupId, | ||
429 | + appId); | ||
430 | + log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}", | ||
431 | + deviceId, Integer.toHexString(mplsgroupId), | ||
432 | + mplsgroupkey, nextId); | ||
433 | + } else { | ||
434 | + // outer group is L3Unicast | ||
435 | + Integer l3groupId = L3UNICASTMASK | (int) portNum; | ||
436 | + int l3gk = L3UNICASTMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum)); | ||
437 | + final GroupKey l3groupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l3gk)); | ||
438 | + outerTtb.group(new DefaultGroupId(l2groupId)); | ||
439 | + // create the l3unicast group description to wait for the | ||
440 | + // l2 interface group to be processed | ||
441 | + GroupBucket l3unicastGroupBucket = | ||
442 | + DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build()); | ||
443 | + outerGrpDesc = new DefaultGroupDescription( | ||
444 | + deviceId, | ||
445 | + GroupDescription.Type.INDIRECT, | ||
446 | + new GroupBuckets(Collections.singletonList( | ||
447 | + l3unicastGroupBucket)), | ||
448 | + l3groupkey, | ||
449 | + l3groupId, | ||
450 | + appId); | ||
451 | + log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}", | ||
452 | + deviceId, Integer.toHexString(l3groupId), | ||
453 | + l3groupkey, nextId); | ||
454 | + } | ||
455 | + | ||
456 | + // store l2groupkey with the groupChainElem for the outer-group that depends on it | ||
457 | + GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1, false); | ||
458 | + updatePendingGroups(l2groupkey, gce); | ||
459 | + | ||
460 | + // create group description for the inner l2interfacegroup | ||
461 | + GroupBucket l2interfaceGroupBucket = | ||
462 | + DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build()); | ||
463 | + GroupDescription l2groupDescription = | ||
464 | + new DefaultGroupDescription( | ||
465 | + deviceId, | ||
466 | + GroupDescription.Type.INDIRECT, | ||
467 | + new GroupBuckets(Collections.singletonList( | ||
468 | + l2interfaceGroupBucket)), | ||
469 | + l2groupkey, | ||
470 | + l2groupId, | ||
471 | + appId); | ||
472 | + log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}", | ||
473 | + deviceId, Integer.toHexString(l2groupId), | ||
474 | + l2groupkey, nextId); | ||
475 | + return new GroupInfo(l2groupDescription, outerGrpDesc); | ||
476 | + | ||
477 | + } | ||
478 | + | ||
479 | + /** | ||
480 | + * As per the OFDPA 2.0 TTP, packets are sent out of ports by using | ||
481 | + * a chain of groups. The broadcast Next Objective passed in by the application | ||
482 | + * has to be broken up into a group chain comprising of an | ||
483 | + * L2 Flood group whose buckets point to L2 Interface groups. | ||
484 | + * | ||
485 | + * @param nextObj the nextObjective of type BROADCAST | ||
486 | + */ | ||
487 | + private void processBroadcastNextObjective(NextObjective nextObj) { | ||
488 | + // break up broadcast next objective to multiple groups | ||
489 | + Collection<TrafficTreatment> buckets = nextObj.next(); | ||
490 | + | ||
491 | + // Read VLAN information from the metadata | ||
492 | + TrafficSelector metadata = nextObj.meta(); | ||
493 | + Criterion criterion = metadata.getCriterion(Criterion.Type.VLAN_VID); | ||
494 | + if (criterion == null) { | ||
495 | + log.warn("Required VLAN ID info in nextObj metadata but not found. Aborting"); | ||
496 | + return; | ||
497 | + } | ||
498 | + VlanId vlanId = ((VlanIdCriterion) criterion).vlanId(); | ||
499 | + | ||
500 | + // each treatment is converted to an L2 interface group | ||
501 | + List<GroupDescription> l2interfaceGroupDescs = new ArrayList<>(); | ||
502 | + List<Deque<GroupKey>> allGroupKeys = new ArrayList<>(); | ||
503 | + for (TrafficTreatment treatment : buckets) { | ||
504 | + TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder(); | ||
505 | + PortNumber portNum = null; | ||
506 | + // ensure that the only allowed treatments are pop-vlan and output | ||
507 | + for (Instruction ins : treatment.allInstructions()) { | ||
508 | + if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
509 | + L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
510 | + switch (l2ins.subtype()) { | ||
511 | + case VLAN_POP: | ||
512 | + newTreatment.add(l2ins); | ||
513 | + break; | ||
514 | + default: | ||
515 | + log.debug("action {} not permitted for broadcast nextObj", | ||
516 | + l2ins.subtype()); | ||
517 | + break; | ||
518 | + } | ||
519 | + } else if (ins.type() == Instruction.Type.OUTPUT) { | ||
520 | + portNum = ((Instructions.OutputInstruction) ins).port(); | ||
521 | + newTreatment.add(ins); | ||
522 | + } else { | ||
523 | + log.debug("TrafficTreatment of type {} not permitted in " | ||
524 | + + " broadcast nextObjective", ins.type()); | ||
525 | + } | ||
526 | + } | ||
527 | + | ||
528 | + // Ensure that all ports of this broadcast nextObj are in the same vlan | ||
529 | + // XXX maybe HA issue here? | ||
530 | + VlanId expectedVlanId = port2Vlan.putIfAbsent(portNum, vlanId); | ||
531 | + if (expectedVlanId != null && !vlanId.equals(expectedVlanId)) { | ||
532 | + log.error("Driver requires all ports in a broadcast nextObj " | ||
533 | + + "to be in the same vlan. Different vlans found " | ||
534 | + + "{} and {}. Aborting group creation", vlanId, expectedVlanId); | ||
535 | + return; | ||
536 | + } | ||
537 | + | ||
538 | + | ||
539 | + // assemble info for l2 interface group | ||
540 | + int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong()); | ||
541 | + final GroupKey l2groupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l2gk)); | ||
542 | + Integer l2groupId = L2INTERFACEMASK | (vlanId.toShort() << 16) | | ||
543 | + (int) portNum.toLong(); | ||
544 | + GroupBucket l2interfaceGroupBucket = | ||
545 | + DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build()); | ||
546 | + GroupDescription l2interfaceGroupDescription = | ||
547 | + new DefaultGroupDescription( | ||
548 | + deviceId, | ||
549 | + GroupDescription.Type.INDIRECT, | ||
550 | + new GroupBuckets(Collections.singletonList( | ||
551 | + l2interfaceGroupBucket)), | ||
552 | + l2groupkey, | ||
553 | + l2groupId, | ||
554 | + nextObj.appId()); | ||
555 | + log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}", | ||
556 | + deviceId, Integer.toHexString(l2groupId), | ||
557 | + l2groupkey, nextObj.id()); | ||
558 | + | ||
559 | + Deque<GroupKey> gkeyChain = new ArrayDeque<>(); | ||
560 | + gkeyChain.addFirst(l2groupkey); | ||
561 | + | ||
562 | + // store the info needed to create this group | ||
563 | + l2interfaceGroupDescs.add(l2interfaceGroupDescription); | ||
564 | + allGroupKeys.add(gkeyChain); | ||
565 | + } | ||
566 | + | ||
567 | + // assemble info for l2 flood group | ||
568 | + Integer l2floodgroupId = L2FLOODMASK | (vlanId.toShort() << 16) | nextObj.id(); | ||
569 | + int l2floodgk = L2FLOODMASK | nextObj.id() << 12; | ||
570 | + final GroupKey l2floodgroupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l2floodgk)); | ||
571 | + // collection of group buckets pointing to all the l2 interface groups | ||
572 | + List<GroupBucket> l2floodBuckets = new ArrayList<>(); | ||
573 | + for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) { | ||
574 | + TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); | ||
575 | + ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId())); | ||
576 | + GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build()); | ||
577 | + l2floodBuckets.add(abucket); | ||
578 | + } | ||
579 | + // create the l2flood group-description to wait for all the | ||
580 | + // l2interface groups to be processed | ||
581 | + GroupDescription l2floodGroupDescription = | ||
582 | + new DefaultGroupDescription( | ||
583 | + deviceId, | ||
584 | + GroupDescription.Type.ALL, | ||
585 | + new GroupBuckets(l2floodBuckets), | ||
586 | + l2floodgroupkey, | ||
587 | + l2floodgroupId, | ||
588 | + nextObj.appId()); | ||
589 | + GroupChainElem gce = new GroupChainElem(l2floodGroupDescription, | ||
590 | + l2interfaceGroupDescs.size(), | ||
591 | + false); | ||
592 | + log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}", | ||
593 | + deviceId, Integer.toHexString(l2floodgroupId), | ||
594 | + l2floodgroupkey, nextObj.id()); | ||
595 | + | ||
596 | + // create objects for local and distributed storage | ||
597 | + allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l2floodgroupkey)); | ||
598 | + OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj); | ||
599 | + | ||
600 | + // store l2floodgroupkey with the ofdpaGroupChain for the nextObjective | ||
601 | + // that depends on it | ||
602 | + updatePendingNextObjective(l2floodgroupkey, ofdpaGrp); | ||
603 | + | ||
604 | + for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) { | ||
605 | + // store all l2groupkeys with the groupChainElem for the l2floodgroup | ||
606 | + // that depends on it | ||
607 | + updatePendingGroups(l2intGrpDesc.appCookie(), gce); | ||
608 | + // send groups for all l2 interface groups | ||
609 | + groupService.addGroup(l2intGrpDesc); | ||
610 | + } | ||
611 | + } | ||
612 | + | ||
613 | + | ||
614 | + | ||
615 | + /** | ||
616 | + * As per the OFDPA 2.0 TTP, packets are sent out of ports by using | ||
617 | + * a chain of groups. The hashed Next Objective passed in by the application | ||
618 | + * has to be broken up into a group chain comprising of an | ||
619 | + * L3 ECMP group as the top level group. Buckets of this group can point | ||
620 | + * to a variety of groups in a group chain, depending on the whether | ||
621 | + * MPLS labels are being pushed or not. | ||
622 | + * <p> | ||
623 | + * NOTE: We do not create MPLS ECMP groups as they are unimplemented in | ||
624 | + * OF-DPA 2.0 (even though it is in the spec). Therefore we do not | ||
625 | + * check the nextObjective meta to see what is matching before being | ||
626 | + * sent to this nextObjective. | ||
627 | + * | ||
628 | + * @param nextObj the nextObjective of type HASHED | ||
629 | + */ | ||
630 | + private void processHashedNextObjective(NextObjective nextObj) { | ||
631 | + // storage for all group keys in the chain of groups created | ||
632 | + List<Deque<GroupKey>> allGroupKeys = new ArrayList<>(); | ||
633 | + List<GroupInfo> unsentGroups = new ArrayList<>(); | ||
634 | + createHashBucketChains(nextObj, allGroupKeys, unsentGroups); | ||
635 | + | ||
636 | + // now we can create the outermost L3 ECMP group | ||
637 | + List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>(); | ||
638 | + for (GroupInfo gi : unsentGroups) { | ||
639 | + // create ECMP bucket to point to the outer group | ||
640 | + TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); | ||
641 | + ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId())); | ||
642 | + GroupBucket sbucket = DefaultGroupBucket | ||
643 | + .createSelectGroupBucket(ttb.build()); | ||
644 | + l3ecmpGroupBuckets.add(sbucket); | ||
645 | + } | ||
646 | + int l3ecmpGroupId = L3ECMPMASK | nextObj.id() << 12; | ||
647 | + GroupKey l3ecmpGroupKey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l3ecmpGroupId)); | ||
648 | + GroupDescription l3ecmpGroupDesc = | ||
649 | + new DefaultGroupDescription( | ||
650 | + deviceId, | ||
651 | + GroupDescription.Type.SELECT, | ||
652 | + new GroupBuckets(l3ecmpGroupBuckets), | ||
653 | + l3ecmpGroupKey, | ||
654 | + l3ecmpGroupId, | ||
655 | + nextObj.appId()); | ||
656 | + GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc, | ||
657 | + l3ecmpGroupBuckets.size(), | ||
658 | + false); | ||
659 | + | ||
660 | + // create objects for local and distributed storage | ||
661 | + allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey)); | ||
662 | + OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj); | ||
663 | + | ||
664 | + // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective | ||
665 | + // that depends on it | ||
666 | + updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp); | ||
667 | + | ||
668 | + log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}", | ||
669 | + deviceId, Integer.toHexString(l3ecmpGroupId), | ||
670 | + l3ecmpGroupKey, nextObj.id()); | ||
671 | + // finally we are ready to send the innermost groups | ||
672 | + for (GroupInfo gi : unsentGroups) { | ||
673 | + log.debug("Sending innermost group {} in group chain on device {} ", | ||
674 | + Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId); | ||
675 | + updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce); | ||
676 | + groupService.addGroup(gi.innerGrpDesc); | ||
677 | + } | ||
678 | + | ||
679 | + } | ||
680 | + | ||
681 | + /** | ||
682 | + * Creates group chains for all buckets in a hashed group, and stores the | ||
683 | + * GroupInfos and GroupKeys for all the groups in the lists passed in, which | ||
684 | + * should be empty. | ||
685 | + * <p> | ||
686 | + * Does not create the top level ECMP group. Does not actually send the | ||
687 | + * groups to the groupService. | ||
688 | + * | ||
689 | + * @param nextObj the Next Objective with buckets that need to be converted | ||
690 | + * to group chains | ||
691 | + * @param allGroupKeys a list to store groupKey for each bucket-group-chain | ||
692 | + * @param unsentGroups a list to store GroupInfo for each bucket-group-chain | ||
693 | + */ | ||
694 | + private void createHashBucketChains(NextObjective nextObj, | ||
695 | + List<Deque<GroupKey>> allGroupKeys, | ||
696 | + List<GroupInfo> unsentGroups) { | ||
697 | + // break up hashed next objective to multiple groups | ||
698 | + Collection<TrafficTreatment> buckets = nextObj.next(); | ||
699 | + | ||
700 | + for (TrafficTreatment bucket : buckets) { | ||
701 | + //figure out how many labels are pushed in each bucket | ||
702 | + int labelsPushed = 0; | ||
703 | + MplsLabel innermostLabel = null; | ||
704 | + for (Instruction ins : bucket.allInstructions()) { | ||
705 | + if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
706 | + L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
707 | + if (l2ins.subtype() == L2ModificationInstruction.L2SubType.MPLS_PUSH) { | ||
708 | + labelsPushed++; | ||
709 | + } | ||
710 | + if (l2ins.subtype() == L2ModificationInstruction.L2SubType.MPLS_LABEL) { | ||
711 | + if (innermostLabel == null) { | ||
712 | + innermostLabel = ((L2ModificationInstruction.ModMplsLabelInstruction) l2ins).mplsLabel(); | ||
713 | + } | ||
714 | + } | ||
715 | + } | ||
716 | + } | ||
717 | + | ||
718 | + Deque<GroupKey> gkeyChain = new ArrayDeque<>(); | ||
719 | + // XXX we only deal with 0 and 1 label push right now | ||
720 | + if (labelsPushed == 0) { | ||
721 | + GroupInfo nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(), | ||
722 | + nextObj.appId(), false, | ||
723 | + nextObj.meta()); | ||
724 | + if (nolabelGroupInfo == null) { | ||
725 | + log.error("Could not process nextObj={} in dev:{}", | ||
726 | + nextObj.id(), deviceId); | ||
727 | + return; | ||
728 | + } | ||
729 | + gkeyChain.addFirst(nolabelGroupInfo.innerGrpDesc.appCookie()); | ||
730 | + gkeyChain.addFirst(nolabelGroupInfo.outerGrpDesc.appCookie()); | ||
731 | + | ||
732 | + // we can't send the inner group description yet, as we have to | ||
733 | + // create the dependent ECMP group first. So we store.. | ||
734 | + unsentGroups.add(nolabelGroupInfo); | ||
735 | + | ||
736 | + } else if (labelsPushed == 1) { | ||
737 | + GroupInfo onelabelGroupInfo = createL2L3Chain(bucket, nextObj.id(), | ||
738 | + nextObj.appId(), true, | ||
739 | + nextObj.meta()); | ||
740 | + if (onelabelGroupInfo == null) { | ||
741 | + log.error("Could not process nextObj={} in dev:{}", | ||
742 | + nextObj.id(), deviceId); | ||
743 | + return; | ||
744 | + } | ||
745 | + // we need to add another group to this chain - the L3VPN group | ||
746 | + TrafficTreatment.Builder l3vpnTtb = DefaultTrafficTreatment.builder(); | ||
747 | + l3vpnTtb.pushMpls() | ||
748 | + .setMpls(innermostLabel) | ||
749 | + .setMplsBos(true) | ||
750 | + .copyTtlOut() | ||
751 | + .group(new DefaultGroupId( | ||
752 | + onelabelGroupInfo.outerGrpDesc.givenGroupId())); | ||
753 | + GroupBucket l3vpnGrpBkt = | ||
754 | + DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build()); | ||
755 | + int l3vpngroupId = L3VPNMASK | l3vpnindex.incrementAndGet(); | ||
756 | + int l3vpngk = L3VPNMASK | nextObj.id() << 12 | l3vpnindex.get(); | ||
757 | + GroupKey l3vpngroupkey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l3vpngk)); | ||
758 | + GroupDescription l3vpnGroupDesc = | ||
759 | + new DefaultGroupDescription( | ||
760 | + deviceId, | ||
761 | + GroupDescription.Type.INDIRECT, | ||
762 | + new GroupBuckets(Collections.singletonList( | ||
763 | + l3vpnGrpBkt)), | ||
764 | + l3vpngroupkey, | ||
765 | + l3vpngroupId, | ||
766 | + nextObj.appId()); | ||
767 | + GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1, false); | ||
768 | + updatePendingGroups(onelabelGroupInfo.outerGrpDesc.appCookie(), l3vpnGce); | ||
769 | + | ||
770 | + gkeyChain.addFirst(onelabelGroupInfo.innerGrpDesc.appCookie()); | ||
771 | + gkeyChain.addFirst(onelabelGroupInfo.outerGrpDesc.appCookie()); | ||
772 | + gkeyChain.addFirst(l3vpngroupkey); | ||
773 | + | ||
774 | + //now we can replace the outerGrpDesc with the one we just created | ||
775 | + onelabelGroupInfo.outerGrpDesc = l3vpnGroupDesc; | ||
776 | + | ||
777 | + // we can't send the innermost group yet, as we have to create | ||
778 | + // the dependent ECMP group first. So we store ... | ||
779 | + unsentGroups.add(onelabelGroupInfo); | ||
780 | + | ||
781 | + log.debug("Trying L3VPN: device:{} gid:{} gkey:{} nextId:{}", | ||
782 | + deviceId, Integer.toHexString(l3vpngroupId), | ||
783 | + l3vpngroupkey, nextObj.id()); | ||
784 | + | ||
785 | + } else { | ||
786 | + log.warn("Driver currently does not handle more than 1 MPLS " | ||
787 | + + "labels. Not processing nextObjective {}", nextObj.id()); | ||
788 | + return; | ||
789 | + } | ||
790 | + | ||
791 | + // all groups in this chain | ||
792 | + allGroupKeys.add(gkeyChain); | ||
793 | + } | ||
794 | + } | ||
795 | + | ||
796 | + /** | ||
797 | + * Adds a bucket to the top level group of a group-chain, and creates the chain. | ||
798 | + * | ||
799 | + * @param nextObjective the next group to add a bucket to | ||
800 | + * @param next the representation of the existing group-chain for this next objective | ||
801 | + */ | ||
802 | + protected void addBucketToGroup(NextObjective nextObjective, NextGroup next) { | ||
803 | + if (nextObjective.type() != NextObjective.Type.HASHED) { | ||
804 | + log.warn("AddBuckets not applied to nextType:{} in dev:{} for next:{}", | ||
805 | + nextObjective.type(), deviceId, nextObjective.id()); | ||
806 | + return; | ||
807 | + } | ||
808 | + if (nextObjective.next().size() > 1) { | ||
809 | + log.warn("Only one bucket can be added at a time"); | ||
810 | + return; | ||
811 | + } | ||
812 | + // storage for all group keys in the chain of groups created | ||
813 | + List<Deque<GroupKey>> allGroupKeys = new ArrayList<>(); | ||
814 | + List<GroupInfo> unsentGroups = new ArrayList<>(); | ||
815 | + createHashBucketChains(nextObjective, allGroupKeys, unsentGroups); | ||
816 | + | ||
817 | + // now we can create the outermost L3 ECMP group bucket to add | ||
818 | + GroupInfo gi = unsentGroups.get(0); // only one bucket, so only one group-chain | ||
819 | + TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); | ||
820 | + ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId())); | ||
821 | + GroupBucket sbucket = DefaultGroupBucket.createSelectGroupBucket(ttb.build()); | ||
822 | + | ||
823 | + // recreate the original L3 ECMP group id and description | ||
824 | + int l3ecmpGroupId = L3ECMPMASK | nextObjective.id() << 12; | ||
825 | + GroupKey l3ecmpGroupKey = new DefaultGroupKey(OFDPA2Pipeline.appKryo.serialize(l3ecmpGroupId)); | ||
826 | + | ||
827 | + // Although GroupDescriptions are not necessary for adding buckets to | ||
828 | + // existing groups, we use one in the GroupChainElem. When the latter is | ||
829 | + // processed, the info will be extracted for the bucketAdd call to groupService | ||
830 | + GroupDescription l3ecmpGroupDesc = | ||
831 | + new DefaultGroupDescription( | ||
832 | + deviceId, | ||
833 | + GroupDescription.Type.SELECT, | ||
834 | + new GroupBuckets(Collections.singletonList(sbucket)), | ||
835 | + l3ecmpGroupKey, | ||
836 | + l3ecmpGroupId, | ||
837 | + nextObjective.appId()); | ||
838 | + GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc, 1, true); | ||
839 | + | ||
840 | + // update original NextGroup with new bucket-chain | ||
841 | + // don't need to update pendingNextObjectives -- group already exists | ||
842 | + Deque<GroupKey> newBucketChain = allGroupKeys.get(0); | ||
843 | + newBucketChain.addFirst(l3ecmpGroupKey); | ||
844 | + List<Deque<GroupKey>> allOriginalKeys = OFDPA2Pipeline.appKryo.deserialize(next.data()); | ||
845 | + allOriginalKeys.add(newBucketChain); | ||
846 | + flowObjectiveStore.putNextGroup(nextObjective.id(), | ||
847 | + new OfdpaNextGroup(allOriginalKeys, nextObjective)); | ||
848 | + | ||
849 | + log.debug("Adding to L3ECMP: device:{} gid:{} gkey:{} nextId:{}", | ||
850 | + deviceId, Integer.toHexString(l3ecmpGroupId), | ||
851 | + l3ecmpGroupKey, nextObjective.id()); | ||
852 | + // send the innermost group | ||
853 | + log.debug("Sending innermost group {} in group chain on device {} ", | ||
854 | + Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId); | ||
855 | + updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce); | ||
856 | + groupService.addGroup(gi.innerGrpDesc); | ||
857 | + | ||
858 | + } | ||
859 | + | ||
860 | + /** | ||
861 | + * Removes the bucket in the top level group of a possible group-chain. Does | ||
862 | + * not remove the groups in a group-chain pointed to by this bucket, as they | ||
863 | + * may be in use (referenced by other groups) elsewhere. | ||
864 | + * | ||
865 | + * @param nextObjective the next group to remove a bucket from | ||
866 | + * @param next the representation of the existing group-chain for this next objective | ||
867 | + */ | ||
868 | + protected void removeBucketFromGroup(NextObjective nextObjective, NextGroup next) { | ||
869 | + if (nextObjective.type() != NextObjective.Type.HASHED) { | ||
870 | + log.warn("RemoveBuckets not applied to nextType:{} in dev:{} for next:{}", | ||
871 | + nextObjective.type(), deviceId, nextObjective.id()); | ||
872 | + return; | ||
873 | + } | ||
874 | + Collection<TrafficTreatment> treatments = nextObjective.next(); | ||
875 | + TrafficTreatment treatment = treatments.iterator().next(); | ||
876 | + // find the bucket to remove by noting the outport, and figuring out the | ||
877 | + // top-level group in the group-chain that indirectly references the port | ||
878 | + PortNumber outport = null; | ||
879 | + for (Instruction ins : treatment.allInstructions()) { | ||
880 | + if (ins instanceof Instructions.OutputInstruction) { | ||
881 | + outport = ((Instructions.OutputInstruction) ins).port(); | ||
882 | + break; | ||
883 | + } | ||
884 | + } | ||
885 | + if (outport == null) { | ||
886 | + log.error("next objective {} has no outport", nextObjective.id()); | ||
887 | + return; | ||
888 | + } | ||
889 | + | ||
890 | + List<Deque<GroupKey>> allgkeys = OFDPA2Pipeline.appKryo.deserialize(next.data()); | ||
891 | + Deque<GroupKey> foundChain = null; | ||
892 | + int index = 0; | ||
893 | + for (Deque<GroupKey> gkeys : allgkeys) { | ||
894 | + GroupKey groupWithPort = gkeys.peekLast(); | ||
895 | + Group group = groupService.getGroup(deviceId, groupWithPort); | ||
896 | + if (group == null) { | ||
897 | + log.warn("Inconsistent group chain"); | ||
898 | + continue; | ||
899 | + } | ||
900 | + // last group in group chain should have a single bucket pointing to port | ||
901 | + List<Instruction> lastIns = group.buckets().buckets().iterator() | ||
902 | + .next().treatment().allInstructions(); | ||
903 | + for (Instruction i : lastIns) { | ||
904 | + if (i instanceof Instructions.OutputInstruction) { | ||
905 | + PortNumber lastport = ((Instructions.OutputInstruction) i).port(); | ||
906 | + if (lastport.equals(outport)) { | ||
907 | + foundChain = gkeys; | ||
908 | + break; | ||
909 | + } | ||
910 | + } | ||
911 | + } | ||
912 | + if (foundChain != null) { | ||
913 | + break; | ||
914 | + } | ||
915 | + index++; | ||
916 | + } | ||
917 | + if (foundChain != null) { | ||
918 | + //first groupkey is the one we want to modify | ||
919 | + GroupKey modGroupKey = foundChain.peekFirst(); | ||
920 | + Group modGroup = groupService.getGroup(deviceId, modGroupKey); | ||
921 | + //second groupkey is the one we wish to remove the reference to | ||
922 | + GroupKey pointedGroupKey = null; | ||
923 | + int i = 0; | ||
924 | + for (GroupKey gk : foundChain) { | ||
925 | + if (i++ == 1) { | ||
926 | + pointedGroupKey = gk; | ||
927 | + break; | ||
928 | + } | ||
929 | + } | ||
930 | + Group pointedGroup = groupService.getGroup(deviceId, pointedGroupKey); | ||
931 | + GroupBucket bucket = DefaultGroupBucket.createSelectGroupBucket( | ||
932 | + DefaultTrafficTreatment.builder() | ||
933 | + .group(pointedGroup.id()) | ||
934 | + .build()); | ||
935 | + GroupBuckets removeBuckets = new GroupBuckets(Collections | ||
936 | + .singletonList(bucket)); | ||
937 | + log.debug("Removing buckets from group id {} for next id {} in device {}", | ||
938 | + modGroup.id(), nextObjective.id(), deviceId); | ||
939 | + groupService.removeBucketsFromGroup(deviceId, modGroupKey, | ||
940 | + removeBuckets, modGroupKey, | ||
941 | + nextObjective.appId()); | ||
942 | + //update store | ||
943 | + allgkeys.remove(index); | ||
944 | + flowObjectiveStore.putNextGroup(nextObjective.id(), | ||
945 | + new OfdpaNextGroup(allgkeys, nextObjective)); | ||
946 | + } else { | ||
947 | + log.warn("Could not find appropriate group-chain for removing bucket" | ||
948 | + + " for next id {} in dev:{}", nextObjective.id(), deviceId); | ||
949 | + } | ||
950 | + } | ||
951 | + | ||
952 | + /** | ||
953 | + * Removes all groups in multiple possible group-chains that represent the next | ||
954 | + * objective. | ||
955 | + * | ||
956 | + * @param nextObjective the next objective to remove | ||
957 | + * @param next the NextGroup that represents the existing group-chain for | ||
958 | + * this next objective | ||
959 | + */ | ||
960 | + protected void removeGroup(NextObjective nextObjective, NextGroup next) { | ||
961 | + List<Deque<GroupKey>> allgkeys = OFDPA2Pipeline.appKryo.deserialize(next.data()); | ||
962 | + allgkeys.forEach(groupChain -> groupChain.forEach(groupKey -> | ||
963 | + groupService.removeGroup(deviceId, groupKey, nextObjective.appId()))); | ||
964 | + flowObjectiveStore.removeNextGroup(nextObjective.id()); | ||
965 | + } | ||
966 | + | ||
967 | + /** | ||
968 | + * Processes next element of a group chain. Assumption is that if this | ||
969 | + * group points to another group, the latter has already been created | ||
970 | + * and this driver has received notification for it. A second assumption is | ||
971 | + * that if there is another group waiting for this group then the appropriate | ||
972 | + * stores already have the information to act upon the notification for the | ||
973 | + * creation of this group. | ||
974 | + * <p> | ||
975 | + * The processing of the GroupChainElement depends on the number of groups | ||
976 | + * this element is waiting on. For all group types other than SIMPLE, a | ||
977 | + * GroupChainElement could be waiting on multiple groups. | ||
978 | + * | ||
979 | + * @param gce the group chain element to be processed next | ||
980 | + */ | ||
981 | + private void processGroupChain(GroupChainElem gce) { | ||
982 | + int waitOnGroups = gce.decrementAndGetGroupsWaitedOn(); | ||
983 | + if (waitOnGroups != 0) { | ||
984 | + log.debug("GCE: {} not ready to be processed", gce); | ||
985 | + return; | ||
986 | + } | ||
987 | + log.debug("GCE: {} ready to be processed", gce); | ||
988 | + if (gce.addBucketToGroup) { | ||
989 | + groupService.addBucketsToGroup(gce.groupDescription.deviceId(), | ||
990 | + gce.groupDescription.appCookie(), | ||
991 | + gce.groupDescription.buckets(), | ||
992 | + gce.groupDescription.appCookie(), | ||
993 | + gce.groupDescription.appId()); | ||
994 | + } else { | ||
995 | + groupService.addGroup(gce.groupDescription); | ||
996 | + } | ||
997 | + } | ||
998 | + | ||
999 | + private class GroupChecker implements Runnable { | ||
1000 | + @Override | ||
1001 | + public void run() { | ||
1002 | + Set<GroupKey> keys = pendingGroups.keySet().stream() | ||
1003 | + .filter(key -> groupService.getGroup(deviceId, key) != null) | ||
1004 | + .collect(Collectors.toSet()); | ||
1005 | + Set<GroupKey> otherkeys = pendingNextObjectives.asMap().keySet().stream() | ||
1006 | + .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null) | ||
1007 | + .collect(Collectors.toSet()); | ||
1008 | + keys.addAll(otherkeys); | ||
1009 | + | ||
1010 | + keys.stream().forEach(key -> | ||
1011 | + processPendingGroupsOrNextObjectives(key, false)); | ||
1012 | + } | ||
1013 | + } | ||
1014 | + | ||
1015 | + private void processPendingGroupsOrNextObjectives(GroupKey key, boolean added) { | ||
1016 | + //first check for group chain | ||
1017 | + Set<GroupChainElem> gceSet = pendingGroups.remove(key); | ||
1018 | + if (gceSet != null) { | ||
1019 | + for (GroupChainElem gce : gceSet) { | ||
1020 | + log.info("Group service {} group key {} in device {}. " | ||
1021 | + + "Processing next group in group chain with group id {}", | ||
1022 | + (added) ? "ADDED" : "processed", | ||
1023 | + key, deviceId, | ||
1024 | + Integer.toHexString(gce.groupDescription.givenGroupId())); | ||
1025 | + processGroupChain(gce); | ||
1026 | + } | ||
1027 | + } else { | ||
1028 | + // otherwise chain complete - check for waiting nextObjectives | ||
1029 | + List<OfdpaNextGroup> nextGrpList = pendingNextObjectives.getIfPresent(key); | ||
1030 | + if (nextGrpList != null) { | ||
1031 | + pendingNextObjectives.invalidate(key); | ||
1032 | + nextGrpList.forEach(nextGrp -> { | ||
1033 | + log.info("Group service {} group key {} in device:{}. " | ||
1034 | + + "Done implementing next objective: {} <<-->> gid:{}", | ||
1035 | + (added) ? "ADDED" : "processed", | ||
1036 | + key, deviceId, nextGrp.nextObjective().id(), | ||
1037 | + Integer.toHexString(groupService.getGroup(deviceId, key) | ||
1038 | + .givenGroupId())); | ||
1039 | + OFDPA2Pipeline.pass(nextGrp.nextObjective()); | ||
1040 | + flowObjectiveStore.putNextGroup(nextGrp.nextObjective().id(), nextGrp); | ||
1041 | + // check if addBuckets waiting for this completion | ||
1042 | + NextObjective pendBkt = pendingBuckets | ||
1043 | + .remove(nextGrp.nextObjective().id()); | ||
1044 | + if (pendBkt != null) { | ||
1045 | + addBucketToGroup(pendBkt, nextGrp); | ||
1046 | + } | ||
1047 | + }); | ||
1048 | + } | ||
1049 | + } | ||
1050 | + } | ||
1051 | + | ||
1052 | + private class InnerGroupListener implements GroupListener { | ||
1053 | + @Override | ||
1054 | + public void event(GroupEvent event) { | ||
1055 | + log.trace("received group event of type {}", event.type()); | ||
1056 | + if (event.type() == GroupEvent.Type.GROUP_ADDED) { | ||
1057 | + GroupKey key = event.subject().appCookie(); | ||
1058 | + processPendingGroupsOrNextObjectives(key, true); | ||
1059 | + } | ||
1060 | + } | ||
1061 | + } | ||
1062 | + | ||
1063 | + /** | ||
1064 | + * Utility class for moving group information around. | ||
1065 | + */ | ||
1066 | + private class GroupInfo { | ||
1067 | + private GroupDescription innerGrpDesc; | ||
1068 | + private GroupDescription outerGrpDesc; | ||
1069 | + | ||
1070 | + GroupInfo(GroupDescription innerGrpDesc, GroupDescription outerGrpDesc) { | ||
1071 | + this.innerGrpDesc = innerGrpDesc; | ||
1072 | + this.outerGrpDesc = outerGrpDesc; | ||
1073 | + } | ||
1074 | + } | ||
1075 | + | ||
1076 | + /** | ||
1077 | + * Represents an entire group-chain that implements a Next-Objective from | ||
1078 | + * the application. The objective is represented as a list of deques, where | ||
1079 | + * each deque is a separate chain of groups. | ||
1080 | + * <p> | ||
1081 | + * For example, an ECMP group with 3 buckets, where each bucket points to | ||
1082 | + * a group chain of L3 Unicast and L2 interface groups will look like this: | ||
1083 | + * <ul> | ||
1084 | + * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last) | ||
1085 | + * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last) | ||
1086 | + * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last) | ||
1087 | + * </ul> | ||
1088 | + * where the first element of each deque is the same, representing the | ||
1089 | + * top level ECMP group, while every other element represents a unique groupKey. | ||
1090 | + * <p> | ||
1091 | + * Also includes information about the next objective that | ||
1092 | + * resulted in this group-chain. | ||
1093 | + * | ||
1094 | + */ | ||
1095 | + protected class OfdpaNextGroup implements NextGroup { | ||
1096 | + private final NextObjective nextObj; | ||
1097 | + private final List<Deque<GroupKey>> gkeys; | ||
1098 | + | ||
1099 | + public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) { | ||
1100 | + this.gkeys = gkeys; | ||
1101 | + this.nextObj = nextObj; | ||
1102 | + } | ||
1103 | + | ||
1104 | + @SuppressWarnings("unused") | ||
1105 | + public List<Deque<GroupKey>> groupKey() { | ||
1106 | + return gkeys; | ||
1107 | + } | ||
1108 | + | ||
1109 | + public NextObjective nextObjective() { | ||
1110 | + return nextObj; | ||
1111 | + } | ||
1112 | + | ||
1113 | + @Override | ||
1114 | + public byte[] data() { | ||
1115 | + return OFDPA2Pipeline.appKryo.serialize(gkeys); | ||
1116 | + } | ||
1117 | + } | ||
1118 | + | ||
1119 | + /** | ||
1120 | + * Represents a group element that is part of a chain of groups. | ||
1121 | + * Stores enough information to create a Group Description to add the group | ||
1122 | + * to the switch by requesting the Group Service. Objects instantiating this | ||
1123 | + * class are meant to be temporary and live as long as it is needed to wait for | ||
1124 | + * preceding groups in the group chain to be created. | ||
1125 | + */ | ||
1126 | + private class GroupChainElem { | ||
1127 | + private GroupDescription groupDescription; | ||
1128 | + private AtomicInteger waitOnGroups; | ||
1129 | + private boolean addBucketToGroup; | ||
1130 | + | ||
1131 | + GroupChainElem(GroupDescription groupDescription, int waitOnGroups, | ||
1132 | + boolean addBucketToGroup) { | ||
1133 | + this.groupDescription = groupDescription; | ||
1134 | + this.waitOnGroups = new AtomicInteger(waitOnGroups); | ||
1135 | + this.addBucketToGroup = addBucketToGroup; | ||
1136 | + } | ||
1137 | + | ||
1138 | + /** | ||
1139 | + * This methods atomically decrements the counter for the number of | ||
1140 | + * groups this GroupChainElement is waiting on, for notifications from | ||
1141 | + * the Group Service. When this method returns a value of 0, this | ||
1142 | + * GroupChainElement is ready to be processed. | ||
1143 | + * | ||
1144 | + * @return integer indication of the number of notifications being waited on | ||
1145 | + */ | ||
1146 | + int decrementAndGetGroupsWaitedOn() { | ||
1147 | + return waitOnGroups.decrementAndGet(); | ||
1148 | + } | ||
1149 | + | ||
1150 | + @Override | ||
1151 | + public String toString() { | ||
1152 | + return (Integer.toHexString(groupDescription.givenGroupId()) + | ||
1153 | + " groupKey: " + groupDescription.appCookie() + | ||
1154 | + " waiting-on-groups: " + waitOnGroups.get() + | ||
1155 | + " addBucketToGroup: " + addBucketToGroup + | ||
1156 | + " device: " + deviceId); | ||
1157 | + } | ||
1158 | + } | ||
1159 | +} |
... | @@ -15,7 +15,6 @@ | ... | @@ -15,7 +15,6 @@ |
15 | */ | 15 | */ |
16 | package org.onosproject.driver.pipeline; | 16 | package org.onosproject.driver.pipeline; |
17 | 17 | ||
18 | -import static org.onlab.util.Tools.groupedThreads; | ||
19 | import static org.slf4j.LoggerFactory.getLogger; | 18 | import static org.slf4j.LoggerFactory.getLogger; |
20 | 19 | ||
21 | import java.util.ArrayDeque; | 20 | import java.util.ArrayDeque; |
... | @@ -24,25 +23,17 @@ import java.util.Collection; | ... | @@ -24,25 +23,17 @@ import java.util.Collection; |
24 | import java.util.Collections; | 23 | import java.util.Collections; |
25 | import java.util.Deque; | 24 | import java.util.Deque; |
26 | import java.util.List; | 25 | import java.util.List; |
27 | -import java.util.Map; | ||
28 | import java.util.Set; | 26 | import java.util.Set; |
29 | import java.util.concurrent.ConcurrentHashMap; | 27 | import java.util.concurrent.ConcurrentHashMap; |
30 | -import java.util.concurrent.CopyOnWriteArrayList; | ||
31 | -import java.util.concurrent.Executors; | ||
32 | -import java.util.concurrent.ScheduledExecutorService; | ||
33 | -import java.util.concurrent.TimeUnit; | ||
34 | -import java.util.concurrent.atomic.AtomicInteger; | ||
35 | -import java.util.stream.Collectors; | ||
36 | 28 | ||
37 | import org.onlab.osgi.ServiceDirectory; | 29 | import org.onlab.osgi.ServiceDirectory; |
38 | import org.onlab.packet.Ethernet; | 30 | import org.onlab.packet.Ethernet; |
39 | import org.onlab.packet.MacAddress; | 31 | import org.onlab.packet.MacAddress; |
40 | -import org.onlab.packet.MplsLabel; | ||
41 | import org.onlab.packet.VlanId; | 32 | import org.onlab.packet.VlanId; |
42 | import org.onlab.util.KryoNamespace; | 33 | import org.onlab.util.KryoNamespace; |
43 | import org.onosproject.core.ApplicationId; | 34 | import org.onosproject.core.ApplicationId; |
44 | import org.onosproject.core.CoreService; | 35 | import org.onosproject.core.CoreService; |
45 | -import org.onosproject.core.DefaultGroupId; | 36 | +import org.onosproject.driver.pipeline.OFDPA2GroupHandler.OfdpaNextGroup; |
46 | import org.onosproject.net.DeviceId; | 37 | import org.onosproject.net.DeviceId; |
47 | import org.onosproject.net.Port; | 38 | import org.onosproject.net.Port; |
48 | import org.onosproject.net.PortNumber; | 39 | import org.onosproject.net.PortNumber; |
... | @@ -62,7 +53,6 @@ import org.onosproject.net.flow.TrafficSelector; | ... | @@ -62,7 +53,6 @@ import org.onosproject.net.flow.TrafficSelector; |
62 | import org.onosproject.net.flow.TrafficTreatment; | 53 | import org.onosproject.net.flow.TrafficTreatment; |
63 | import org.onosproject.net.flow.criteria.Criteria; | 54 | import org.onosproject.net.flow.criteria.Criteria; |
64 | import org.onosproject.net.flow.criteria.Criterion; | 55 | import org.onosproject.net.flow.criteria.Criterion; |
65 | -import org.onosproject.net.flow.criteria.Criterion.Type; | ||
66 | import org.onosproject.net.flow.criteria.EthCriterion; | 56 | import org.onosproject.net.flow.criteria.EthCriterion; |
67 | import org.onosproject.net.flow.criteria.EthTypeCriterion; | 57 | import org.onosproject.net.flow.criteria.EthTypeCriterion; |
68 | import org.onosproject.net.flow.criteria.IPCriterion; | 58 | import org.onosproject.net.flow.criteria.IPCriterion; |
... | @@ -74,8 +64,6 @@ import org.onosproject.net.flow.instructions.Instruction; | ... | @@ -74,8 +64,6 @@ import org.onosproject.net.flow.instructions.Instruction; |
74 | import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; | 64 | import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; |
75 | import org.onosproject.net.flow.instructions.L2ModificationInstruction; | 65 | import org.onosproject.net.flow.instructions.L2ModificationInstruction; |
76 | import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType; | 66 | import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType; |
77 | -import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction; | ||
78 | -import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction; | ||
79 | import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction; | 67 | import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction; |
80 | import org.onosproject.net.flowobjective.FilteringObjective; | 68 | import org.onosproject.net.flowobjective.FilteringObjective; |
81 | import org.onosproject.net.flowobjective.FlowObjectiveStore; | 69 | import org.onosproject.net.flowobjective.FlowObjectiveStore; |
... | @@ -83,31 +71,18 @@ import org.onosproject.net.flowobjective.ForwardingObjective; | ... | @@ -83,31 +71,18 @@ import org.onosproject.net.flowobjective.ForwardingObjective; |
83 | import org.onosproject.net.flowobjective.NextObjective; | 71 | import org.onosproject.net.flowobjective.NextObjective; |
84 | import org.onosproject.net.flowobjective.Objective; | 72 | import org.onosproject.net.flowobjective.Objective; |
85 | import org.onosproject.net.flowobjective.ObjectiveError; | 73 | import org.onosproject.net.flowobjective.ObjectiveError; |
86 | -import org.onosproject.net.group.DefaultGroupBucket; | ||
87 | -import org.onosproject.net.group.DefaultGroupDescription; | ||
88 | import org.onosproject.net.group.DefaultGroupKey; | 74 | import org.onosproject.net.group.DefaultGroupKey; |
89 | import org.onosproject.net.group.Group; | 75 | import org.onosproject.net.group.Group; |
90 | -import org.onosproject.net.group.GroupBucket; | ||
91 | -import org.onosproject.net.group.GroupBuckets; | ||
92 | -import org.onosproject.net.group.GroupDescription; | ||
93 | -import org.onosproject.net.group.GroupEvent; | ||
94 | import org.onosproject.net.group.GroupKey; | 76 | import org.onosproject.net.group.GroupKey; |
95 | -import org.onosproject.net.group.GroupListener; | ||
96 | import org.onosproject.net.group.GroupService; | 77 | import org.onosproject.net.group.GroupService; |
97 | import org.onosproject.store.serializers.KryoNamespaces; | 78 | import org.onosproject.store.serializers.KryoNamespaces; |
98 | import org.slf4j.Logger; | 79 | import org.slf4j.Logger; |
99 | 80 | ||
100 | -import com.google.common.cache.Cache; | ||
101 | -import com.google.common.cache.CacheBuilder; | ||
102 | -import com.google.common.cache.RemovalCause; | ||
103 | -import com.google.common.cache.RemovalNotification; | ||
104 | - | ||
105 | /** | 81 | /** |
106 | * Driver for Broadcom's OF-DPA v2.0 TTP. | 82 | * Driver for Broadcom's OF-DPA v2.0 TTP. |
107 | * | 83 | * |
108 | */ | 84 | */ |
109 | public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeliner { | 85 | public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeliner { |
110 | - | ||
111 | protected static final int PORT_TABLE = 0; | 86 | protected static final int PORT_TABLE = 0; |
112 | protected static final int VLAN_TABLE = 10; | 87 | protected static final int VLAN_TABLE = 10; |
113 | protected static final int TMAC_TABLE = 20; | 88 | protected static final int TMAC_TABLE = 20; |
... | @@ -124,22 +99,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -124,22 +99,6 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
124 | protected static final int DEFAULT_PRIORITY = 0x8000; | 99 | protected static final int DEFAULT_PRIORITY = 0x8000; |
125 | protected static final int LOWEST_PRIORITY = 0x0; | 100 | protected static final int LOWEST_PRIORITY = 0x0; |
126 | 101 | ||
127 | - /* | ||
128 | - * OFDPA requires group-id's to have a certain form. | ||
129 | - * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid> | ||
130 | - * L3 Unicast Groups have <4bits-2><28bits-index> | ||
131 | - * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index> | ||
132 | - * L3 ECMP Groups have <4bits-7><28bits-index> | ||
133 | - * L2 Flood Groups have <4bits-4><12bits-vlanid><16bits-index> | ||
134 | - * L3 VPN Groups have <4bits-9><4bits-2><24bits-index> | ||
135 | - */ | ||
136 | - private static final int L2INTERFACEMASK = 0x0; | ||
137 | - private static final int L3UNICASTMASK = 0x20000000; | ||
138 | - private static final int MPLSINTERFACEMASK = 0x90000000; | ||
139 | - private static final int L3ECMPMASK = 0x70000000; | ||
140 | - private static final int L2FLOODMASK = 0x40000000; | ||
141 | - private static final int L3VPNMASK = 0x92000000; | ||
142 | - | ||
143 | private final Logger log = getLogger(getClass()); | 102 | private final Logger log = getLogger(getClass()); |
144 | private ServiceDirectory serviceDirectory; | 103 | private ServiceDirectory serviceDirectory; |
145 | protected FlowRuleService flowRuleService; | 104 | protected FlowRuleService flowRuleService; |
... | @@ -149,7 +108,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -149,7 +108,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
149 | protected DeviceId deviceId; | 108 | protected DeviceId deviceId; |
150 | protected ApplicationId driverId; | 109 | protected ApplicationId driverId; |
151 | protected DeviceService deviceService; | 110 | protected DeviceService deviceService; |
152 | - protected KryoNamespace appKryo = new KryoNamespace.Builder() | 111 | + protected static KryoNamespace appKryo = new KryoNamespace.Builder() |
153 | .register(KryoNamespaces.API) | 112 | .register(KryoNamespaces.API) |
154 | .register(GroupKey.class) | 113 | .register(GroupKey.class) |
155 | .register(DefaultGroupKey.class) | 114 | .register(DefaultGroupKey.class) |
... | @@ -158,67 +117,36 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -158,67 +117,36 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
158 | .register(ArrayDeque.class) | 117 | .register(ArrayDeque.class) |
159 | .build(); | 118 | .build(); |
160 | 119 | ||
161 | - private Cache<GroupKey, List<OfdpaNextGroup>> pendingNextObjectives; | 120 | + protected OFDPA2GroupHandler ofdpa2GroupHandler; |
162 | - private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups; | ||
163 | 121 | ||
164 | - private ScheduledExecutorService groupChecker = | ||
165 | - Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner", | ||
166 | - "ofdpa2-%d")); | ||
167 | private Set<IPCriterion> sentIpFilters = Collections.newSetFromMap( | 122 | private Set<IPCriterion> sentIpFilters = Collections.newSetFromMap( |
168 | - new ConcurrentHashMap<IPCriterion, Boolean>()); | 123 | + new ConcurrentHashMap<>()); |
169 | - | ||
170 | - // local stores for port-vlan mapping | ||
171 | - Map<PortNumber, VlanId> port2Vlan = new ConcurrentHashMap<PortNumber, VlanId>(); | ||
172 | - Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<VlanId, | ||
173 | - Set<PortNumber>>(); | ||
174 | - | ||
175 | - // local store for pending bucketAdds - by design there can only be one | ||
176 | - // pending bucket for a group | ||
177 | - ConcurrentHashMap<Integer, NextObjective> pendingBuckets = new ConcurrentHashMap<>(); | ||
178 | - | ||
179 | - // index number for group creation | ||
180 | - AtomicInteger l3vpnindex = new AtomicInteger(0); | ||
181 | - | ||
182 | 124 | ||
183 | @Override | 125 | @Override |
184 | public void init(DeviceId deviceId, PipelinerContext context) { | 126 | public void init(DeviceId deviceId, PipelinerContext context) { |
185 | this.serviceDirectory = context.directory(); | 127 | this.serviceDirectory = context.directory(); |
186 | this.deviceId = deviceId; | 128 | this.deviceId = deviceId; |
187 | 129 | ||
188 | - pendingNextObjectives = CacheBuilder.newBuilder() | 130 | + // Initialize OFDPA group handler |
189 | - .expireAfterWrite(20, TimeUnit.SECONDS) | 131 | + ofdpa2GroupHandler = new OFDPA2GroupHandler(); |
190 | - .removalListener(( | 132 | + ofdpa2GroupHandler.init(deviceId, context); |
191 | - RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> { | ||
192 | - if (notification.getCause() == RemovalCause.EXPIRED) { | ||
193 | - notification.getValue().forEach(ofdpaNextGrp -> | ||
194 | - fail(ofdpaNextGrp.nextObj, | ||
195 | - ObjectiveError.GROUPINSTALLATIONFAILED)); | ||
196 | - | ||
197 | - } | ||
198 | - }).build(); | ||
199 | - | ||
200 | - groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS); | ||
201 | - pendingGroups = new ConcurrentHashMap<GroupKey, Set<GroupChainElem>>(); | ||
202 | 133 | ||
203 | coreService = serviceDirectory.get(CoreService.class); | 134 | coreService = serviceDirectory.get(CoreService.class); |
204 | flowRuleService = serviceDirectory.get(FlowRuleService.class); | 135 | flowRuleService = serviceDirectory.get(FlowRuleService.class); |
205 | groupService = serviceDirectory.get(GroupService.class); | 136 | groupService = serviceDirectory.get(GroupService.class); |
206 | flowObjectiveStore = context.store(); | 137 | flowObjectiveStore = context.store(); |
207 | deviceService = serviceDirectory.get(DeviceService.class); | 138 | deviceService = serviceDirectory.get(DeviceService.class); |
208 | - groupService.addListener(new InnerGroupListener()); | ||
209 | 139 | ||
210 | driverId = coreService.registerApplication( | 140 | driverId = coreService.registerApplication( |
211 | "org.onosproject.driver.OFDPA2Pipeline"); | 141 | "org.onosproject.driver.OFDPA2Pipeline"); |
212 | 142 | ||
213 | - // OF-DPA does not require initializing the pipeline as it puts default | ||
214 | - // rules automatically in the hardware. However emulation of OFDPA in | ||
215 | - // software switches does require table-miss-entries. | ||
216 | initializePipeline(); | 143 | initializePipeline(); |
217 | - | ||
218 | } | 144 | } |
219 | 145 | ||
220 | protected void initializePipeline() { | 146 | protected void initializePipeline() { |
221 | - | 147 | + // OF-DPA does not require initializing the pipeline as it puts default |
148 | + // rules automatically in the hardware. However emulation of OFDPA in | ||
149 | + // software switches does require table-miss-entries. | ||
222 | } | 150 | } |
223 | 151 | ||
224 | ////////////////////////////////////// | 152 | ////////////////////////////////////// |
... | @@ -288,19 +216,19 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -288,19 +216,19 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
288 | } | 216 | } |
289 | log.debug("Processing NextObjective id{} in dev{} - add group", | 217 | log.debug("Processing NextObjective id{} in dev{} - add group", |
290 | nextObjective.id(), deviceId); | 218 | nextObjective.id(), deviceId); |
291 | - addGroup(nextObjective); | 219 | + ofdpa2GroupHandler.addGroup(nextObjective); |
292 | break; | 220 | break; |
293 | case ADD_TO_EXISTING: | 221 | case ADD_TO_EXISTING: |
294 | if (nextGroup != null) { | 222 | if (nextGroup != null) { |
295 | log.debug("Processing NextObjective id{} in dev{} - add bucket", | 223 | log.debug("Processing NextObjective id{} in dev{} - add bucket", |
296 | nextObjective.id(), deviceId); | 224 | nextObjective.id(), deviceId); |
297 | - addBucketToGroup(nextObjective, nextGroup); | 225 | + ofdpa2GroupHandler.addBucketToGroup(nextObjective, nextGroup); |
298 | } else { | 226 | } else { |
299 | // it is possible that group-chain has not been fully created yet | 227 | // it is possible that group-chain has not been fully created yet |
300 | log.debug("Waiting to add bucket to group for next-id:{} in dev:{}", | 228 | log.debug("Waiting to add bucket to group for next-id:{} in dev:{}", |
301 | nextObjective.id(), deviceId); | 229 | nextObjective.id(), deviceId); |
302 | // by design only one pending bucket is allowed for the group | 230 | // by design only one pending bucket is allowed for the group |
303 | - pendingBuckets.put(nextObjective.id(), nextObjective); | 231 | + ofdpa2GroupHandler.pendingBuckets.put(nextObjective.id(), nextObjective); |
304 | } | 232 | } |
305 | break; | 233 | break; |
306 | case REMOVE: | 234 | case REMOVE: |
... | @@ -311,7 +239,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -311,7 +239,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
311 | } | 239 | } |
312 | log.debug("Processing NextObjective id{} in dev{} - remove group", | 240 | log.debug("Processing NextObjective id{} in dev{} - remove group", |
313 | nextObjective.id(), deviceId); | 241 | nextObjective.id(), deviceId); |
314 | - removeGroup(nextObjective, nextGroup); | 242 | + ofdpa2GroupHandler.removeGroup(nextObjective, nextGroup); |
315 | break; | 243 | break; |
316 | case REMOVE_FROM_EXISTING: | 244 | case REMOVE_FROM_EXISTING: |
317 | if (nextGroup == null) { | 245 | if (nextGroup == null) { |
... | @@ -321,7 +249,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -321,7 +249,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
321 | } | 249 | } |
322 | log.debug("Processing NextObjective id{} in dev{} - remove bucket", | 250 | log.debug("Processing NextObjective id{} in dev{} - remove bucket", |
323 | nextObjective.id(), deviceId); | 251 | nextObjective.id(), deviceId); |
324 | - removeBucketFromGroup(nextObjective, nextGroup); | 252 | + ofdpa2GroupHandler.removeBucketFromGroup(nextObjective, nextGroup); |
325 | break; | 253 | break; |
326 | default: | 254 | default: |
327 | log.warn("Unsupported operation {}", nextObjective.op()); | 255 | log.warn("Unsupported operation {}", nextObjective.op()); |
... | @@ -514,13 +442,13 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -514,13 +442,13 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
514 | 442 | ||
515 | for (PortNumber pnum : portnums) { | 443 | for (PortNumber pnum : portnums) { |
516 | // update storage | 444 | // update storage |
517 | - port2Vlan.put(pnum, storeVlan); | 445 | + ofdpa2GroupHandler.port2Vlan.put(pnum, storeVlan); |
518 | - Set<PortNumber> vlanPorts = vlan2Port.get(storeVlan); | 446 | + Set<PortNumber> vlanPorts = ofdpa2GroupHandler.vlan2Port.get(storeVlan); |
519 | if (vlanPorts == null) { | 447 | if (vlanPorts == null) { |
520 | vlanPorts = Collections.newSetFromMap( | 448 | vlanPorts = Collections.newSetFromMap( |
521 | new ConcurrentHashMap<PortNumber, Boolean>()); | 449 | new ConcurrentHashMap<PortNumber, Boolean>()); |
522 | vlanPorts.add(pnum); | 450 | vlanPorts.add(pnum); |
523 | - vlan2Port.put(storeVlan, vlanPorts); | 451 | + ofdpa2GroupHandler.vlan2Port.put(storeVlan, vlanPorts); |
524 | } else { | 452 | } else { |
525 | vlanPorts.add(pnum); | 453 | vlanPorts.add(pnum); |
526 | } | 454 | } |
... | @@ -711,12 +639,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -711,12 +639,9 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
711 | TrafficSelector selector = fwd.selector(); | 639 | TrafficSelector selector = fwd.selector(); |
712 | EthTypeCriterion ethType = (EthTypeCriterion) selector | 640 | EthTypeCriterion ethType = (EthTypeCriterion) selector |
713 | .getCriterion(Criterion.Type.ETH_TYPE); | 641 | .getCriterion(Criterion.Type.ETH_TYPE); |
714 | - if ((ethType == null) || | 642 | + return !((ethType == null) || |
715 | ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) && | 643 | ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) && |
716 | - (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) { | 644 | + (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))); |
717 | - return false; | ||
718 | - } | ||
719 | - return true; | ||
720 | } | 645 | } |
721 | 646 | ||
722 | private boolean isSupportedEthDstObjective(ForwardingObjective fwd) { | 647 | private boolean isSupportedEthDstObjective(ForwardingObjective fwd) { |
... | @@ -725,10 +650,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -725,10 +650,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
725 | .getCriterion(Criterion.Type.ETH_DST); | 650 | .getCriterion(Criterion.Type.ETH_DST); |
726 | VlanIdCriterion vlanId = (VlanIdCriterion) selector | 651 | VlanIdCriterion vlanId = (VlanIdCriterion) selector |
727 | .getCriterion(Criterion.Type.VLAN_VID); | 652 | .getCriterion(Criterion.Type.VLAN_VID); |
728 | - if (ethDst == null && vlanId == null) { | 653 | + return !(ethDst == null && vlanId == null); |
729 | - return false; | ||
730 | - } | ||
731 | - return true; | ||
732 | } | 654 | } |
733 | 655 | ||
734 | /** | 656 | /** |
... | @@ -742,7 +664,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -742,7 +664,7 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
742 | EthTypeCriterion ethType = | 664 | EthTypeCriterion ethType = |
743 | (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE); | 665 | (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE); |
744 | 666 | ||
745 | - int forTableId = -1; | 667 | + int forTableId; |
746 | TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder(); | 668 | TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder(); |
747 | if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) { | 669 | if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) { |
748 | filteredSelector.matchEthType(Ethernet.TYPE_IPV4) | 670 | filteredSelector.matchEthType(Ethernet.TYPE_IPV4) |
... | @@ -926,1053 +848,15 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -926,1053 +848,15 @@ public class OFDPA2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
926 | return null; | 848 | return null; |
927 | } | 849 | } |
928 | 850 | ||
929 | - private void pass(Objective obj) { | 851 | + protected static void pass(Objective obj) { |
930 | if (obj.context().isPresent()) { | 852 | if (obj.context().isPresent()) { |
931 | obj.context().get().onSuccess(obj); | 853 | obj.context().get().onSuccess(obj); |
932 | } | 854 | } |
933 | } | 855 | } |
934 | 856 | ||
935 | - protected void fail(Objective obj, ObjectiveError error) { | 857 | + protected static void fail(Objective obj, ObjectiveError error) { |
936 | if (obj.context().isPresent()) { | 858 | if (obj.context().isPresent()) { |
937 | obj.context().get().onError(obj, error); | 859 | obj.context().get().onError(obj, error); |
938 | } | 860 | } |
939 | } | 861 | } |
940 | - | ||
941 | - ////////////////////////////////////// | ||
942 | - // Group handling | ||
943 | - ////////////////////////////////////// | ||
944 | - | ||
945 | - private void addGroup(NextObjective nextObjective) { | ||
946 | - switch (nextObjective.type()) { | ||
947 | - case SIMPLE: | ||
948 | - Collection<TrafficTreatment> treatments = nextObjective.next(); | ||
949 | - if (treatments.size() != 1) { | ||
950 | - log.error("Next Objectives of type Simple should only have a " | ||
951 | - + "single Traffic Treatment. Next Objective Id:{}", | ||
952 | - nextObjective.id()); | ||
953 | - fail(nextObjective, ObjectiveError.BADPARAMS); | ||
954 | - return; | ||
955 | - } | ||
956 | - processSimpleNextObjective(nextObjective); | ||
957 | - break; | ||
958 | - case BROADCAST: | ||
959 | - processBroadcastNextObjective(nextObjective); | ||
960 | - break; | ||
961 | - case HASHED: | ||
962 | - processHashedNextObjective(nextObjective); | ||
963 | - break; | ||
964 | - case FAILOVER: | ||
965 | - fail(nextObjective, ObjectiveError.UNSUPPORTED); | ||
966 | - log.warn("Unsupported next objective type {}", nextObjective.type()); | ||
967 | - break; | ||
968 | - default: | ||
969 | - fail(nextObjective, ObjectiveError.UNKNOWN); | ||
970 | - log.warn("Unknown next objective type {}", nextObjective.type()); | ||
971 | - } | ||
972 | - } | ||
973 | - | ||
974 | - /** | ||
975 | - * As per the OFDPA 2.0 TTP, packets are sent out of ports by using | ||
976 | - * a chain of groups. The simple Next Objective passed | ||
977 | - * in by the application has to be broken up into a group chain | ||
978 | - * comprising of an L3 Unicast Group that points to an L2 Interface | ||
979 | - * Group which in-turn points to an output port. In some cases, the simple | ||
980 | - * next Objective can just be an L2 interface without the need for chaining. | ||
981 | - * | ||
982 | - * @param nextObj the nextObjective of type SIMPLE | ||
983 | - */ | ||
984 | - private void processSimpleNextObjective(NextObjective nextObj) { | ||
985 | - TrafficTreatment treatment = nextObj.next().iterator().next(); | ||
986 | - // determine if plain L2 or L3->L2 | ||
987 | - boolean plainL2 = true; | ||
988 | - for (Instruction ins : treatment.allInstructions()) { | ||
989 | - if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
990 | - L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
991 | - if (l2ins.subtype() == L2SubType.ETH_DST || | ||
992 | - l2ins.subtype() == L2SubType.ETH_SRC) { | ||
993 | - plainL2 = false; | ||
994 | - break; | ||
995 | - } | ||
996 | - } | ||
997 | - } | ||
998 | - | ||
999 | - if (plainL2) { | ||
1000 | - createL2InterfaceGroup(nextObj); | ||
1001 | - return; | ||
1002 | - } | ||
1003 | - | ||
1004 | - // break up simple next objective to GroupChain objects | ||
1005 | - GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(), | ||
1006 | - nextObj.appId(), false, | ||
1007 | - nextObj.meta()); | ||
1008 | - if (groupInfo == null) { | ||
1009 | - log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId); | ||
1010 | - return; | ||
1011 | - } | ||
1012 | - // create object for local and distributed storage | ||
1013 | - Deque<GroupKey> gkeyChain = new ArrayDeque<>(); | ||
1014 | - gkeyChain.addFirst(groupInfo.innerGrpDesc.appCookie()); | ||
1015 | - gkeyChain.addFirst(groupInfo.outerGrpDesc.appCookie()); | ||
1016 | - OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup( | ||
1017 | - Collections.singletonList(gkeyChain), | ||
1018 | - nextObj); | ||
1019 | - | ||
1020 | - // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it | ||
1021 | - updatePendingNextObjective(groupInfo.outerGrpDesc.appCookie(), ofdpaGrp); | ||
1022 | - | ||
1023 | - // now we are ready to send the l2 groupDescription (inner), as all the stores | ||
1024 | - // that will get async replies have been updated. By waiting to update | ||
1025 | - // the stores, we prevent nasty race conditions. | ||
1026 | - groupService.addGroup(groupInfo.innerGrpDesc); | ||
1027 | - } | ||
1028 | - | ||
1029 | - private void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) { | ||
1030 | - List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>(); | ||
1031 | - nextList.add(value); | ||
1032 | - List<OfdpaNextGroup> ret = pendingNextObjectives.asMap() | ||
1033 | - .putIfAbsent(key, nextList); | ||
1034 | - if (ret != null) { | ||
1035 | - ret.add(value); | ||
1036 | - } | ||
1037 | - } | ||
1038 | - | ||
1039 | - private void updatePendingGroups(GroupKey gkey, GroupChainElem gce) { | ||
1040 | - Set<GroupChainElem> gceSet = Collections.newSetFromMap( | ||
1041 | - new ConcurrentHashMap<GroupChainElem, Boolean>()); | ||
1042 | - gceSet.add(gce); | ||
1043 | - Set<GroupChainElem> retval = pendingGroups.putIfAbsent(gkey, gceSet); | ||
1044 | - if (retval != null) { | ||
1045 | - retval.add(gce); | ||
1046 | - } | ||
1047 | - } | ||
1048 | - | ||
1049 | - /** | ||
1050 | - * Creates a simple L2 Interface Group. | ||
1051 | - * | ||
1052 | - * @param nextObj the next Objective | ||
1053 | - */ | ||
1054 | - private void createL2InterfaceGroup(NextObjective nextObj) { | ||
1055 | - // only allowed actions are vlan pop and outport | ||
1056 | - TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); | ||
1057 | - PortNumber portNum = null; | ||
1058 | - for (Instruction ins : nextObj.next().iterator().next().allInstructions()) { | ||
1059 | - if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
1060 | - L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
1061 | - switch (l2ins.subtype()) { | ||
1062 | - case VLAN_POP: | ||
1063 | - ttb.add(l2ins); | ||
1064 | - break; | ||
1065 | - default: | ||
1066 | - break; | ||
1067 | - } | ||
1068 | - } else if (ins.type() == Instruction.Type.OUTPUT) { | ||
1069 | - portNum = ((OutputInstruction) ins).port(); | ||
1070 | - ttb.add(ins); | ||
1071 | - } else { | ||
1072 | - log.warn("Driver does not handle this type of TrafficTreatment" | ||
1073 | - + " instruction in simple nextObjectives: {}", ins.type()); | ||
1074 | - } | ||
1075 | - } | ||
1076 | - //use the vlanid associated with the port | ||
1077 | - VlanId vlanid = port2Vlan.get(portNum); | ||
1078 | - | ||
1079 | - if (vlanid == null && nextObj.meta() != null) { | ||
1080 | - // use metadata vlan info if available | ||
1081 | - Criterion vidCriterion = nextObj.meta().getCriterion(Type.VLAN_VID); | ||
1082 | - if (vidCriterion != null) { | ||
1083 | - vlanid = ((VlanIdCriterion) vidCriterion).vlanId(); | ||
1084 | - } | ||
1085 | - } | ||
1086 | - | ||
1087 | - if (vlanid == null) { | ||
1088 | - log.error("Driver cannot process an L2/L3 group chain without " | ||
1089 | - + "egress vlan information for dev: {} port:{}", | ||
1090 | - deviceId, portNum); | ||
1091 | - return; | ||
1092 | - } | ||
1093 | - | ||
1094 | - // assemble information for ofdpa l2interface group | ||
1095 | - Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum.toLong(); | ||
1096 | - // a globally unique groupkey that is different for ports in the same devices | ||
1097 | - // but different for the same portnumber on different devices. Also different | ||
1098 | - // for the various group-types created out of the same next objective. | ||
1099 | - int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong()); | ||
1100 | - final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk)); | ||
1101 | - | ||
1102 | - // create group description for the l2interfacegroup | ||
1103 | - GroupBucket l2interfaceGroupBucket = | ||
1104 | - DefaultGroupBucket.createIndirectGroupBucket(ttb.build()); | ||
1105 | - GroupDescription l2groupDescription = | ||
1106 | - new DefaultGroupDescription( | ||
1107 | - deviceId, | ||
1108 | - GroupDescription.Type.INDIRECT, | ||
1109 | - new GroupBuckets(Collections.singletonList( | ||
1110 | - l2interfaceGroupBucket)), | ||
1111 | - l2groupkey, | ||
1112 | - l2groupId, | ||
1113 | - nextObj.appId()); | ||
1114 | - log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}", | ||
1115 | - deviceId, Integer.toHexString(l2groupId), | ||
1116 | - l2groupkey, nextObj.id()); | ||
1117 | - | ||
1118 | - // create object for local and distributed storage | ||
1119 | - Deque<GroupKey> singleKey = new ArrayDeque<>(); | ||
1120 | - singleKey.addFirst(l2groupkey); | ||
1121 | - OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup( | ||
1122 | - Collections.singletonList(singleKey), | ||
1123 | - nextObj); | ||
1124 | - | ||
1125 | - // store l2groupkey for the nextObjective that depends on it | ||
1126 | - updatePendingNextObjective(l2groupkey, ofdpaGrp); | ||
1127 | - // send the group description to the group service | ||
1128 | - groupService.addGroup(l2groupDescription); | ||
1129 | - } | ||
1130 | - | ||
1131 | - /** | ||
1132 | - * Creates one of two possible group-chains from the treatment | ||
1133 | - * passed in. Depending on the MPLS boolean, this method either creates | ||
1134 | - * an L3Unicast Group --> L2Interface Group, if mpls is false; | ||
1135 | - * or MPLSInterface Group --> L2Interface Group, if mpls is true; | ||
1136 | - * The returned 'inner' group description is always the L2 Interface group. | ||
1137 | - * | ||
1138 | - * @param treatment that needs to be broken up to create the group chain | ||
1139 | - * @param nextId of the next objective that needs this group chain | ||
1140 | - * @param appId of the application that sent this next objective | ||
1141 | - * @param mpls determines if L3Unicast or MPLSInterface group is created | ||
1142 | - * @param meta metadata passed in by the application as part of the nextObjective | ||
1143 | - * @return GroupInfo containing the GroupDescription of the | ||
1144 | - * L2Interface group(inner) and the GroupDescription of the (outer) | ||
1145 | - * L3Unicast/MPLSInterface group. May return null if there is an | ||
1146 | - * error in processing the chain | ||
1147 | - */ | ||
1148 | - private GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId, | ||
1149 | - ApplicationId appId, boolean mpls, | ||
1150 | - TrafficSelector meta) { | ||
1151 | - // for the l2interface group, get vlan and port info | ||
1152 | - // for the outer group, get the src/dst mac, and vlan info | ||
1153 | - TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder(); | ||
1154 | - TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder(); | ||
1155 | - VlanId vlanid = null; | ||
1156 | - long portNum = 0; | ||
1157 | - boolean setVlan = false, popVlan = false; | ||
1158 | - for (Instruction ins : treatment.allInstructions()) { | ||
1159 | - if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
1160 | - L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
1161 | - switch (l2ins.subtype()) { | ||
1162 | - case ETH_DST: | ||
1163 | - outerTtb.setEthDst(((ModEtherInstruction) l2ins).mac()); | ||
1164 | - break; | ||
1165 | - case ETH_SRC: | ||
1166 | - outerTtb.setEthSrc(((ModEtherInstruction) l2ins).mac()); | ||
1167 | - break; | ||
1168 | - case VLAN_ID: | ||
1169 | - vlanid = ((ModVlanIdInstruction) l2ins).vlanId(); | ||
1170 | - outerTtb.setVlanId(vlanid); | ||
1171 | - setVlan = true; | ||
1172 | - break; | ||
1173 | - case VLAN_POP: | ||
1174 | - innerTtb.popVlan(); | ||
1175 | - popVlan = true; | ||
1176 | - break; | ||
1177 | - case DEC_MPLS_TTL: | ||
1178 | - case MPLS_LABEL: | ||
1179 | - case MPLS_POP: | ||
1180 | - case MPLS_PUSH: | ||
1181 | - case VLAN_PCP: | ||
1182 | - case VLAN_PUSH: | ||
1183 | - default: | ||
1184 | - break; | ||
1185 | - } | ||
1186 | - } else if (ins.type() == Instruction.Type.OUTPUT) { | ||
1187 | - portNum = ((OutputInstruction) ins).port().toLong(); | ||
1188 | - innerTtb.add(ins); | ||
1189 | - } else { | ||
1190 | - log.warn("Driver does not handle this type of TrafficTreatment" | ||
1191 | - + " instruction in nextObjectives: {}", ins.type()); | ||
1192 | - } | ||
1193 | - } | ||
1194 | - | ||
1195 | - if (vlanid == null && meta != null) { | ||
1196 | - // use metadata if available | ||
1197 | - Criterion vidCriterion = meta.getCriterion(Type.VLAN_VID); | ||
1198 | - if (vidCriterion != null) { | ||
1199 | - vlanid = ((VlanIdCriterion) vidCriterion).vlanId(); | ||
1200 | - } | ||
1201 | - // if vlan is not set, use the vlan in metadata for outerTtb | ||
1202 | - if (vlanid != null && !setVlan) { | ||
1203 | - outerTtb.setVlanId(vlanid); | ||
1204 | - } | ||
1205 | - } | ||
1206 | - | ||
1207 | - if (vlanid == null) { | ||
1208 | - log.error("Driver cannot process an L2/L3 group chain without " | ||
1209 | - + "egress vlan information for dev: {} port:{}", | ||
1210 | - deviceId, portNum); | ||
1211 | - return null; | ||
1212 | - } | ||
1213 | - | ||
1214 | - if (!setVlan && !popVlan) { | ||
1215 | - // untagged outgoing port | ||
1216 | - TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder(); | ||
1217 | - temp.popVlan(); | ||
1218 | - innerTtb.build().allInstructions().forEach(i -> temp.add(i)); | ||
1219 | - innerTtb = temp; | ||
1220 | - } | ||
1221 | - | ||
1222 | - // assemble information for ofdpa l2interface group | ||
1223 | - Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | (int) portNum; | ||
1224 | - // a globally unique groupkey that is different for ports in the same devices | ||
1225 | - // but different for the same portnumber on different devices. Also different | ||
1226 | - // for the various group-types created out of the same next objective. | ||
1227 | - int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum); | ||
1228 | - final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk)); | ||
1229 | - | ||
1230 | - // assemble information for outer group | ||
1231 | - GroupDescription outerGrpDesc = null; | ||
1232 | - if (mpls) { | ||
1233 | - // outer group is MPLSInteface | ||
1234 | - Integer mplsgroupId = MPLSINTERFACEMASK | (int) portNum; | ||
1235 | - // using mplsinterfacemask in groupkey to differentiate from l2interface | ||
1236 | - int mplsgk = MPLSINTERFACEMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum)); | ||
1237 | - final GroupKey mplsgroupkey = new DefaultGroupKey(appKryo.serialize(mplsgk)); | ||
1238 | - outerTtb.group(new DefaultGroupId(l2groupId)); | ||
1239 | - // create the mpls-interface group description to wait for the | ||
1240 | - // l2 interface group to be processed | ||
1241 | - GroupBucket mplsinterfaceGroupBucket = | ||
1242 | - DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build()); | ||
1243 | - outerGrpDesc = new DefaultGroupDescription( | ||
1244 | - deviceId, | ||
1245 | - GroupDescription.Type.INDIRECT, | ||
1246 | - new GroupBuckets(Collections.singletonList( | ||
1247 | - mplsinterfaceGroupBucket)), | ||
1248 | - mplsgroupkey, | ||
1249 | - mplsgroupId, | ||
1250 | - appId); | ||
1251 | - log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}", | ||
1252 | - deviceId, Integer.toHexString(mplsgroupId), | ||
1253 | - mplsgroupkey, nextId); | ||
1254 | - } else { | ||
1255 | - // outer group is L3Unicast | ||
1256 | - Integer l3groupId = L3UNICASTMASK | (int) portNum; | ||
1257 | - int l3gk = L3UNICASTMASK | (0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum)); | ||
1258 | - final GroupKey l3groupkey = new DefaultGroupKey(appKryo.serialize(l3gk)); | ||
1259 | - outerTtb.group(new DefaultGroupId(l2groupId)); | ||
1260 | - // create the l3unicast group description to wait for the | ||
1261 | - // l2 interface group to be processed | ||
1262 | - GroupBucket l3unicastGroupBucket = | ||
1263 | - DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build()); | ||
1264 | - outerGrpDesc = new DefaultGroupDescription( | ||
1265 | - deviceId, | ||
1266 | - GroupDescription.Type.INDIRECT, | ||
1267 | - new GroupBuckets(Collections.singletonList( | ||
1268 | - l3unicastGroupBucket)), | ||
1269 | - l3groupkey, | ||
1270 | - l3groupId, | ||
1271 | - appId); | ||
1272 | - log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}", | ||
1273 | - deviceId, Integer.toHexString(l3groupId), | ||
1274 | - l3groupkey, nextId); | ||
1275 | - } | ||
1276 | - | ||
1277 | - // store l2groupkey with the groupChainElem for the outer-group that depends on it | ||
1278 | - GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1, false); | ||
1279 | - updatePendingGroups(l2groupkey, gce); | ||
1280 | - | ||
1281 | - // create group description for the inner l2interfacegroup | ||
1282 | - GroupBucket l2interfaceGroupBucket = | ||
1283 | - DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build()); | ||
1284 | - GroupDescription l2groupDescription = | ||
1285 | - new DefaultGroupDescription( | ||
1286 | - deviceId, | ||
1287 | - GroupDescription.Type.INDIRECT, | ||
1288 | - new GroupBuckets(Collections.singletonList( | ||
1289 | - l2interfaceGroupBucket)), | ||
1290 | - l2groupkey, | ||
1291 | - l2groupId, | ||
1292 | - appId); | ||
1293 | - log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}", | ||
1294 | - deviceId, Integer.toHexString(l2groupId), | ||
1295 | - l2groupkey, nextId); | ||
1296 | - return new GroupInfo(l2groupDescription, outerGrpDesc); | ||
1297 | - | ||
1298 | - } | ||
1299 | - | ||
1300 | - /** | ||
1301 | - * As per the OFDPA 2.0 TTP, packets are sent out of ports by using | ||
1302 | - * a chain of groups. The broadcast Next Objective passed in by the application | ||
1303 | - * has to be broken up into a group chain comprising of an | ||
1304 | - * L2 Flood group whose buckets point to L2 Interface groups. | ||
1305 | - * | ||
1306 | - * @param nextObj the nextObjective of type BROADCAST | ||
1307 | - */ | ||
1308 | - private void processBroadcastNextObjective(NextObjective nextObj) { | ||
1309 | - // break up broadcast next objective to multiple groups | ||
1310 | - Collection<TrafficTreatment> buckets = nextObj.next(); | ||
1311 | - | ||
1312 | - // each treatment is converted to an L2 interface group | ||
1313 | - VlanId vlanid = null; | ||
1314 | - List<GroupDescription> l2interfaceGroupDescs = new ArrayList<>(); | ||
1315 | - List<Deque<GroupKey>> allGroupKeys = new ArrayList<>(); | ||
1316 | - for (TrafficTreatment treatment : buckets) { | ||
1317 | - TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder(); | ||
1318 | - PortNumber portNum = null; | ||
1319 | - // ensure that the only allowed treatments are pop-vlan and output | ||
1320 | - for (Instruction ins : treatment.allInstructions()) { | ||
1321 | - if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
1322 | - L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
1323 | - switch (l2ins.subtype()) { | ||
1324 | - case VLAN_POP: | ||
1325 | - newTreatment.add(l2ins); | ||
1326 | - break; | ||
1327 | - default: | ||
1328 | - log.debug("action {} not permitted for broadcast nextObj", | ||
1329 | - l2ins.subtype()); | ||
1330 | - break; | ||
1331 | - } | ||
1332 | - } else if (ins.type() == Instruction.Type.OUTPUT) { | ||
1333 | - portNum = ((OutputInstruction) ins).port(); | ||
1334 | - newTreatment.add(ins); | ||
1335 | - } else { | ||
1336 | - log.debug("TrafficTreatment of type {} not permitted in " | ||
1337 | - + " broadcast nextObjective", ins.type()); | ||
1338 | - } | ||
1339 | - } | ||
1340 | - | ||
1341 | - // also ensure that all ports are in the same vlan | ||
1342 | - // XXX maybe HA issue here? | ||
1343 | - VlanId thisvlanid = port2Vlan.get(portNum); | ||
1344 | - if (vlanid == null) { | ||
1345 | - vlanid = thisvlanid; | ||
1346 | - } else { | ||
1347 | - if (!vlanid.equals(thisvlanid)) { | ||
1348 | - log.error("Driver requires all ports in a broadcast nextObj " | ||
1349 | - + "to be in the same vlan. Different vlans found " | ||
1350 | - + "{} and {}. Aborting group creation", vlanid, thisvlanid); | ||
1351 | - return; | ||
1352 | - } | ||
1353 | - } | ||
1354 | - | ||
1355 | - // assemble info for l2 interface group | ||
1356 | - int l2gk = 0x0ffffff & (deviceId.hashCode() << 8 | (int) portNum.toLong()); | ||
1357 | - final GroupKey l2groupkey = new DefaultGroupKey(appKryo.serialize(l2gk)); | ||
1358 | - Integer l2groupId = L2INTERFACEMASK | (vlanid.toShort() << 16) | | ||
1359 | - (int) portNum.toLong(); | ||
1360 | - GroupBucket l2interfaceGroupBucket = | ||
1361 | - DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build()); | ||
1362 | - GroupDescription l2interfaceGroupDescription = | ||
1363 | - new DefaultGroupDescription( | ||
1364 | - deviceId, | ||
1365 | - GroupDescription.Type.INDIRECT, | ||
1366 | - new GroupBuckets(Collections.singletonList( | ||
1367 | - l2interfaceGroupBucket)), | ||
1368 | - l2groupkey, | ||
1369 | - l2groupId, | ||
1370 | - nextObj.appId()); | ||
1371 | - log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}", | ||
1372 | - deviceId, Integer.toHexString(l2groupId), | ||
1373 | - l2groupkey, nextObj.id()); | ||
1374 | - | ||
1375 | - Deque<GroupKey> gkeyChain = new ArrayDeque<>(); | ||
1376 | - gkeyChain.addFirst(l2groupkey); | ||
1377 | - | ||
1378 | - // store the info needed to create this group | ||
1379 | - l2interfaceGroupDescs.add(l2interfaceGroupDescription); | ||
1380 | - allGroupKeys.add(gkeyChain); | ||
1381 | - } | ||
1382 | - | ||
1383 | - // assemble info for l2 flood group | ||
1384 | - Integer l2floodgroupId = L2FLOODMASK | (vlanid.toShort() << 16) | nextObj.id(); | ||
1385 | - int l2floodgk = L2FLOODMASK | nextObj.id() << 12; | ||
1386 | - final GroupKey l2floodgroupkey = new DefaultGroupKey(appKryo.serialize(l2floodgk)); | ||
1387 | - // collection of group buckets pointing to all the l2 interface groups | ||
1388 | - List<GroupBucket> l2floodBuckets = new ArrayList<>(); | ||
1389 | - for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) { | ||
1390 | - TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); | ||
1391 | - ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId())); | ||
1392 | - GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build()); | ||
1393 | - l2floodBuckets.add(abucket); | ||
1394 | - } | ||
1395 | - // create the l2flood group-description to wait for all the | ||
1396 | - // l2interface groups to be processed | ||
1397 | - GroupDescription l2floodGroupDescription = | ||
1398 | - new DefaultGroupDescription( | ||
1399 | - deviceId, | ||
1400 | - GroupDescription.Type.ALL, | ||
1401 | - new GroupBuckets(l2floodBuckets), | ||
1402 | - l2floodgroupkey, | ||
1403 | - l2floodgroupId, | ||
1404 | - nextObj.appId()); | ||
1405 | - GroupChainElem gce = new GroupChainElem(l2floodGroupDescription, | ||
1406 | - l2interfaceGroupDescs.size(), | ||
1407 | - false); | ||
1408 | - log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}", | ||
1409 | - deviceId, Integer.toHexString(l2floodgroupId), | ||
1410 | - l2floodgroupkey, nextObj.id()); | ||
1411 | - | ||
1412 | - // create objects for local and distributed storage | ||
1413 | - allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l2floodgroupkey)); | ||
1414 | - OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj); | ||
1415 | - | ||
1416 | - // store l2floodgroupkey with the ofdpaGroupChain for the nextObjective | ||
1417 | - // that depends on it | ||
1418 | - updatePendingNextObjective(l2floodgroupkey, ofdpaGrp); | ||
1419 | - | ||
1420 | - for (GroupDescription l2intGrpDesc : l2interfaceGroupDescs) { | ||
1421 | - // store all l2groupkeys with the groupChainElem for the l2floodgroup | ||
1422 | - // that depends on it | ||
1423 | - updatePendingGroups(l2intGrpDesc.appCookie(), gce); | ||
1424 | - // send groups for all l2 interface groups | ||
1425 | - groupService.addGroup(l2intGrpDesc); | ||
1426 | - } | ||
1427 | - } | ||
1428 | - | ||
1429 | - /** | ||
1430 | - * Utility class for moving group information around. | ||
1431 | - * | ||
1432 | - */ | ||
1433 | - private class GroupInfo { | ||
1434 | - private GroupDescription innerGrpDesc; | ||
1435 | - private GroupDescription outerGrpDesc; | ||
1436 | - | ||
1437 | - GroupInfo(GroupDescription innerGrpDesc, GroupDescription outerGrpDesc) { | ||
1438 | - this.innerGrpDesc = innerGrpDesc; | ||
1439 | - this.outerGrpDesc = outerGrpDesc; | ||
1440 | - } | ||
1441 | - } | ||
1442 | - | ||
1443 | - /** | ||
1444 | - * As per the OFDPA 2.0 TTP, packets are sent out of ports by using | ||
1445 | - * a chain of groups. The hashed Next Objective passed in by the application | ||
1446 | - * has to be broken up into a group chain comprising of an | ||
1447 | - * L3 ECMP group as the top level group. Buckets of this group can point | ||
1448 | - * to a variety of groups in a group chain, depending on the whether | ||
1449 | - * MPLS labels are being pushed or not. | ||
1450 | - * <p> | ||
1451 | - * NOTE: We do not create MPLS ECMP groups as they are unimplemented in | ||
1452 | - * OF-DPA 2.0 (even though it is in the spec). Therefore we do not | ||
1453 | - * check the nextObjective meta to see what is matching before being | ||
1454 | - * sent to this nextObjective. | ||
1455 | - * | ||
1456 | - * @param nextObj the nextObjective of type HASHED | ||
1457 | - */ | ||
1458 | - private void processHashedNextObjective(NextObjective nextObj) { | ||
1459 | - // storage for all group keys in the chain of groups created | ||
1460 | - List<Deque<GroupKey>> allGroupKeys = new ArrayList<>(); | ||
1461 | - List<GroupInfo> unsentGroups = new ArrayList<>(); | ||
1462 | - createHashBucketChains(nextObj, allGroupKeys, unsentGroups); | ||
1463 | - | ||
1464 | - // now we can create the outermost L3 ECMP group | ||
1465 | - List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>(); | ||
1466 | - for (GroupInfo gi : unsentGroups) { | ||
1467 | - // create ECMP bucket to point to the outer group | ||
1468 | - TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); | ||
1469 | - ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId())); | ||
1470 | - GroupBucket sbucket = DefaultGroupBucket | ||
1471 | - .createSelectGroupBucket(ttb.build()); | ||
1472 | - l3ecmpGroupBuckets.add(sbucket); | ||
1473 | - } | ||
1474 | - int l3ecmpGroupId = L3ECMPMASK | nextObj.id() << 12; | ||
1475 | - GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId)); | ||
1476 | - GroupDescription l3ecmpGroupDesc = | ||
1477 | - new DefaultGroupDescription( | ||
1478 | - deviceId, | ||
1479 | - GroupDescription.Type.SELECT, | ||
1480 | - new GroupBuckets(l3ecmpGroupBuckets), | ||
1481 | - l3ecmpGroupKey, | ||
1482 | - l3ecmpGroupId, | ||
1483 | - nextObj.appId()); | ||
1484 | - GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc, | ||
1485 | - l3ecmpGroupBuckets.size(), | ||
1486 | - false); | ||
1487 | - | ||
1488 | - // create objects for local and distributed storage | ||
1489 | - allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey)); | ||
1490 | - OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj); | ||
1491 | - | ||
1492 | - // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective | ||
1493 | - // that depends on it | ||
1494 | - updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp); | ||
1495 | - | ||
1496 | - log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}", | ||
1497 | - deviceId, Integer.toHexString(l3ecmpGroupId), | ||
1498 | - l3ecmpGroupKey, nextObj.id()); | ||
1499 | - // finally we are ready to send the innermost groups | ||
1500 | - for (GroupInfo gi : unsentGroups) { | ||
1501 | - log.debug("Sending innermost group {} in group chain on device {} ", | ||
1502 | - Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId); | ||
1503 | - updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce); | ||
1504 | - groupService.addGroup(gi.innerGrpDesc); | ||
1505 | - } | ||
1506 | - | ||
1507 | - } | ||
1508 | - | ||
1509 | - /** | ||
1510 | - * Creates group chains for all buckets in a hashed group, and stores the | ||
1511 | - * GroupInfos and GroupKeys for all the groups in the lists passed in, which | ||
1512 | - * should be empty. | ||
1513 | - * <p> | ||
1514 | - * Does not create the top level ECMP group. Does not actually send the | ||
1515 | - * groups to the groupService. | ||
1516 | - * | ||
1517 | - * @param nextObj the Next Objective with buckets that need to be converted | ||
1518 | - * to group chains | ||
1519 | - * @param allGroupKeys a list to store groupKey for each bucket-group-chain | ||
1520 | - * @param unsentGroups a list to store GroupInfo for each bucket-group-chain | ||
1521 | - */ | ||
1522 | - private void createHashBucketChains(NextObjective nextObj, | ||
1523 | - List<Deque<GroupKey>> allGroupKeys, | ||
1524 | - List<GroupInfo> unsentGroups) { | ||
1525 | - // break up hashed next objective to multiple groups | ||
1526 | - Collection<TrafficTreatment> buckets = nextObj.next(); | ||
1527 | - | ||
1528 | - for (TrafficTreatment bucket : buckets) { | ||
1529 | - //figure out how many labels are pushed in each bucket | ||
1530 | - int labelsPushed = 0; | ||
1531 | - MplsLabel innermostLabel = null; | ||
1532 | - for (Instruction ins : bucket.allInstructions()) { | ||
1533 | - if (ins.type() == Instruction.Type.L2MODIFICATION) { | ||
1534 | - L2ModificationInstruction l2ins = (L2ModificationInstruction) ins; | ||
1535 | - if (l2ins.subtype() == L2SubType.MPLS_PUSH) { | ||
1536 | - labelsPushed++; | ||
1537 | - } | ||
1538 | - if (l2ins.subtype() == L2SubType.MPLS_LABEL) { | ||
1539 | - if (innermostLabel == null) { | ||
1540 | - innermostLabel = ((ModMplsLabelInstruction) l2ins).mplsLabel(); | ||
1541 | - } | ||
1542 | - } | ||
1543 | - } | ||
1544 | - } | ||
1545 | - | ||
1546 | - Deque<GroupKey> gkeyChain = new ArrayDeque<>(); | ||
1547 | - // XXX we only deal with 0 and 1 label push right now | ||
1548 | - if (labelsPushed == 0) { | ||
1549 | - GroupInfo nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(), | ||
1550 | - nextObj.appId(), false, | ||
1551 | - nextObj.meta()); | ||
1552 | - if (nolabelGroupInfo == null) { | ||
1553 | - log.error("Could not process nextObj={} in dev:{}", | ||
1554 | - nextObj.id(), deviceId); | ||
1555 | - return; | ||
1556 | - } | ||
1557 | - gkeyChain.addFirst(nolabelGroupInfo.innerGrpDesc.appCookie()); | ||
1558 | - gkeyChain.addFirst(nolabelGroupInfo.outerGrpDesc.appCookie()); | ||
1559 | - | ||
1560 | - // we can't send the inner group description yet, as we have to | ||
1561 | - // create the dependent ECMP group first. So we store.. | ||
1562 | - unsentGroups.add(nolabelGroupInfo); | ||
1563 | - | ||
1564 | - } else if (labelsPushed == 1) { | ||
1565 | - GroupInfo onelabelGroupInfo = createL2L3Chain(bucket, nextObj.id(), | ||
1566 | - nextObj.appId(), true, | ||
1567 | - nextObj.meta()); | ||
1568 | - if (onelabelGroupInfo == null) { | ||
1569 | - log.error("Could not process nextObj={} in dev:{}", | ||
1570 | - nextObj.id(), deviceId); | ||
1571 | - return; | ||
1572 | - } | ||
1573 | - // we need to add another group to this chain - the L3VPN group | ||
1574 | - TrafficTreatment.Builder l3vpnTtb = DefaultTrafficTreatment.builder(); | ||
1575 | - l3vpnTtb.pushMpls() | ||
1576 | - .setMpls(innermostLabel) | ||
1577 | - .setMplsBos(true) | ||
1578 | - .copyTtlOut() | ||
1579 | - .group(new DefaultGroupId( | ||
1580 | - onelabelGroupInfo.outerGrpDesc.givenGroupId())); | ||
1581 | - GroupBucket l3vpnGrpBkt = | ||
1582 | - DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build()); | ||
1583 | - int l3vpngroupId = L3VPNMASK | l3vpnindex.incrementAndGet(); | ||
1584 | - int l3vpngk = L3VPNMASK | nextObj.id() << 12 | l3vpnindex.get(); | ||
1585 | - GroupKey l3vpngroupkey = new DefaultGroupKey(appKryo.serialize(l3vpngk)); | ||
1586 | - GroupDescription l3vpnGroupDesc = | ||
1587 | - new DefaultGroupDescription( | ||
1588 | - deviceId, | ||
1589 | - GroupDescription.Type.INDIRECT, | ||
1590 | - new GroupBuckets(Collections.singletonList( | ||
1591 | - l3vpnGrpBkt)), | ||
1592 | - l3vpngroupkey, | ||
1593 | - l3vpngroupId, | ||
1594 | - nextObj.appId()); | ||
1595 | - GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1, false); | ||
1596 | - updatePendingGroups(onelabelGroupInfo.outerGrpDesc.appCookie(), l3vpnGce); | ||
1597 | - | ||
1598 | - gkeyChain.addFirst(onelabelGroupInfo.innerGrpDesc.appCookie()); | ||
1599 | - gkeyChain.addFirst(onelabelGroupInfo.outerGrpDesc.appCookie()); | ||
1600 | - gkeyChain.addFirst(l3vpngroupkey); | ||
1601 | - | ||
1602 | - //now we can replace the outerGrpDesc with the one we just created | ||
1603 | - onelabelGroupInfo.outerGrpDesc = l3vpnGroupDesc; | ||
1604 | - | ||
1605 | - // we can't send the innermost group yet, as we have to create | ||
1606 | - // the dependent ECMP group first. So we store ... | ||
1607 | - unsentGroups.add(onelabelGroupInfo); | ||
1608 | - | ||
1609 | - log.debug("Trying L3VPN: device:{} gid:{} gkey:{} nextId:{}", | ||
1610 | - deviceId, Integer.toHexString(l3vpngroupId), | ||
1611 | - l3vpngroupkey, nextObj.id()); | ||
1612 | - | ||
1613 | - } else { | ||
1614 | - log.warn("Driver currently does not handle more than 1 MPLS " | ||
1615 | - + "labels. Not processing nextObjective {}", nextObj.id()); | ||
1616 | - return; | ||
1617 | - } | ||
1618 | - | ||
1619 | - // all groups in this chain | ||
1620 | - allGroupKeys.add(gkeyChain); | ||
1621 | - } | ||
1622 | - } | ||
1623 | - | ||
1624 | - /** | ||
1625 | - * Adds a bucket to the top level group of a group-chain, and creates the chain. | ||
1626 | - * | ||
1627 | - * @param nextObjective the next group to add a bucket to | ||
1628 | - * @param next the representation of the existing group-chain for this next objective | ||
1629 | - */ | ||
1630 | - private void addBucketToGroup(NextObjective nextObjective, NextGroup next) { | ||
1631 | - if (nextObjective.type() != NextObjective.Type.HASHED) { | ||
1632 | - log.warn("AddBuckets not applied to nextType:{} in dev:{} for next:{}", | ||
1633 | - nextObjective.type(), deviceId, nextObjective.id()); | ||
1634 | - return; | ||
1635 | - } | ||
1636 | - if (nextObjective.next().size() > 1) { | ||
1637 | - log.warn("Only one bucket can be added at a time"); | ||
1638 | - return; | ||
1639 | - } | ||
1640 | - // storage for all group keys in the chain of groups created | ||
1641 | - List<Deque<GroupKey>> allGroupKeys = new ArrayList<>(); | ||
1642 | - List<GroupInfo> unsentGroups = new ArrayList<>(); | ||
1643 | - createHashBucketChains(nextObjective, allGroupKeys, unsentGroups); | ||
1644 | - | ||
1645 | - // now we can create the outermost L3 ECMP group bucket to add | ||
1646 | - GroupInfo gi = unsentGroups.get(0); // only one bucket, so only one group-chain | ||
1647 | - TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder(); | ||
1648 | - ttb.group(new DefaultGroupId(gi.outerGrpDesc.givenGroupId())); | ||
1649 | - GroupBucket sbucket = DefaultGroupBucket.createSelectGroupBucket(ttb.build()); | ||
1650 | - | ||
1651 | - // recreate the original L3 ECMP group id and description | ||
1652 | - int l3ecmpGroupId = L3ECMPMASK | nextObjective.id() << 12; | ||
1653 | - GroupKey l3ecmpGroupKey = new DefaultGroupKey(appKryo.serialize(l3ecmpGroupId)); | ||
1654 | - | ||
1655 | - // Although GroupDescriptions are not necessary for adding buckets to | ||
1656 | - // existing groups, we use one in the GroupChainElem. When the latter is | ||
1657 | - // processed, the info will be extracted for the bucketAdd call to groupService | ||
1658 | - GroupDescription l3ecmpGroupDesc = | ||
1659 | - new DefaultGroupDescription( | ||
1660 | - deviceId, | ||
1661 | - GroupDescription.Type.SELECT, | ||
1662 | - new GroupBuckets(Collections.singletonList(sbucket)), | ||
1663 | - l3ecmpGroupKey, | ||
1664 | - l3ecmpGroupId, | ||
1665 | - nextObjective.appId()); | ||
1666 | - GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc, 1, true); | ||
1667 | - | ||
1668 | - // update original NextGroup with new bucket-chain | ||
1669 | - // don't need to update pendingNextObjectives -- group already exists | ||
1670 | - Deque<GroupKey> newBucketChain = allGroupKeys.get(0); | ||
1671 | - newBucketChain.addFirst(l3ecmpGroupKey); | ||
1672 | - List<Deque<GroupKey>> allOriginalKeys = appKryo.deserialize(next.data()); | ||
1673 | - allOriginalKeys.add(newBucketChain); | ||
1674 | - flowObjectiveStore.putNextGroup(nextObjective.id(), | ||
1675 | - new OfdpaNextGroup(allOriginalKeys, nextObjective)); | ||
1676 | - | ||
1677 | - log.debug("Adding to L3ECMP: device:{} gid:{} gkey:{} nextId:{}", | ||
1678 | - deviceId, Integer.toHexString(l3ecmpGroupId), | ||
1679 | - l3ecmpGroupKey, nextObjective.id()); | ||
1680 | - // send the innermost group | ||
1681 | - log.debug("Sending innermost group {} in group chain on device {} ", | ||
1682 | - Integer.toHexString(gi.innerGrpDesc.givenGroupId()), deviceId); | ||
1683 | - updatePendingGroups(gi.outerGrpDesc.appCookie(), l3ecmpGce); | ||
1684 | - groupService.addGroup(gi.innerGrpDesc); | ||
1685 | - | ||
1686 | - } | ||
1687 | - | ||
1688 | - /** | ||
1689 | - * Removes the bucket in the top level group of a possible group-chain. Does | ||
1690 | - * not remove the groups in a group-chain pointed to by this bucket, as they | ||
1691 | - * may be in use (referenced by other groups) elsewhere. | ||
1692 | - * | ||
1693 | - * @param nextObjective the next group to remove a bucket from | ||
1694 | - * @param next the representation of the existing group-chain for this next objective | ||
1695 | - */ | ||
1696 | - private void removeBucketFromGroup(NextObjective nextObjective, NextGroup next) { | ||
1697 | - if (nextObjective.type() != NextObjective.Type.HASHED) { | ||
1698 | - log.warn("RemoveBuckets not applied to nextType:{} in dev:{} for next:{}", | ||
1699 | - nextObjective.type(), deviceId, nextObjective.id()); | ||
1700 | - return; | ||
1701 | - } | ||
1702 | - Collection<TrafficTreatment> treatments = nextObjective.next(); | ||
1703 | - TrafficTreatment treatment = treatments.iterator().next(); | ||
1704 | - // find the bucket to remove by noting the outport, and figuring out the | ||
1705 | - // top-level group in the group-chain that indirectly references the port | ||
1706 | - PortNumber outport = null; | ||
1707 | - for (Instruction ins : treatment.allInstructions()) { | ||
1708 | - if (ins instanceof OutputInstruction) { | ||
1709 | - outport = ((OutputInstruction) ins).port(); | ||
1710 | - break; | ||
1711 | - } | ||
1712 | - } | ||
1713 | - if (outport == null) { | ||
1714 | - log.error("next objective {} has no outport", nextObjective.id()); | ||
1715 | - return; | ||
1716 | - } | ||
1717 | - | ||
1718 | - List<Deque<GroupKey>> allgkeys = appKryo.deserialize(next.data()); | ||
1719 | - Deque<GroupKey> foundChain = null; | ||
1720 | - int index = 0; | ||
1721 | - for (Deque<GroupKey> gkeys : allgkeys) { | ||
1722 | - GroupKey groupWithPort = gkeys.peekLast(); | ||
1723 | - Group group = groupService.getGroup(deviceId, groupWithPort); | ||
1724 | - if (group == null) { | ||
1725 | - log.warn("Inconsistent group chain"); | ||
1726 | - continue; | ||
1727 | - } | ||
1728 | - // last group in group chain should have a single bucket pointing to port | ||
1729 | - List<Instruction> lastIns = group.buckets().buckets().iterator() | ||
1730 | - .next().treatment().allInstructions(); | ||
1731 | - for (Instruction i : lastIns) { | ||
1732 | - if (i instanceof OutputInstruction) { | ||
1733 | - PortNumber lastport = ((OutputInstruction) i).port(); | ||
1734 | - if (lastport.equals(outport)) { | ||
1735 | - foundChain = gkeys; | ||
1736 | - break; | ||
1737 | - } | ||
1738 | - } | ||
1739 | - } | ||
1740 | - if (foundChain != null) { | ||
1741 | - break; | ||
1742 | - } | ||
1743 | - index++; | ||
1744 | - } | ||
1745 | - if (foundChain != null) { | ||
1746 | - //first groupkey is the one we want to modify | ||
1747 | - GroupKey modGroupKey = foundChain.peekFirst(); | ||
1748 | - Group modGroup = groupService.getGroup(deviceId, modGroupKey); | ||
1749 | - //second groupkey is the one we wish to remove the reference to | ||
1750 | - GroupKey pointedGroupKey = null; | ||
1751 | - int i = 0; | ||
1752 | - for (GroupKey gk : foundChain) { | ||
1753 | - if (i++ == 1) { | ||
1754 | - pointedGroupKey = gk; | ||
1755 | - break; | ||
1756 | - } | ||
1757 | - } | ||
1758 | - Group pointedGroup = groupService.getGroup(deviceId, pointedGroupKey); | ||
1759 | - GroupBucket bucket = DefaultGroupBucket.createSelectGroupBucket( | ||
1760 | - DefaultTrafficTreatment.builder() | ||
1761 | - .group(pointedGroup.id()) | ||
1762 | - .build()); | ||
1763 | - GroupBuckets removeBuckets = new GroupBuckets(Collections | ||
1764 | - .singletonList(bucket)); | ||
1765 | - log.debug("Removing buckets from group id {} for next id {} in device {}", | ||
1766 | - modGroup.id(), nextObjective.id(), deviceId); | ||
1767 | - groupService.removeBucketsFromGroup(deviceId, modGroupKey, | ||
1768 | - removeBuckets, modGroupKey, | ||
1769 | - nextObjective.appId()); | ||
1770 | - //update store | ||
1771 | - allgkeys.remove(index); | ||
1772 | - flowObjectiveStore.putNextGroup(nextObjective.id(), | ||
1773 | - new OfdpaNextGroup(allgkeys, nextObjective)); | ||
1774 | - } else { | ||
1775 | - log.warn("Could not find appropriate group-chain for removing bucket" | ||
1776 | - + " for next id {} in dev:{}", nextObjective.id(), deviceId); | ||
1777 | - } | ||
1778 | - } | ||
1779 | - | ||
1780 | - /** | ||
1781 | - * Removes all groups in multiple possible group-chains that represent the next | ||
1782 | - * objective. | ||
1783 | - * | ||
1784 | - * @param nextObjective the next objective to remove | ||
1785 | - * @param next the NextGroup that represents the existing group-chain for | ||
1786 | - * this next objective | ||
1787 | - */ | ||
1788 | - private void removeGroup(NextObjective nextObjective, NextGroup next) { | ||
1789 | - List<Deque<GroupKey>> allgkeys = appKryo.deserialize(next.data()); | ||
1790 | - allgkeys.forEach(groupChain -> { | ||
1791 | - groupChain.forEach(groupKey -> | ||
1792 | - groupService.removeGroup(deviceId, groupKey, nextObjective.appId())); | ||
1793 | - }); | ||
1794 | - flowObjectiveStore.removeNextGroup(nextObjective.id()); | ||
1795 | - } | ||
1796 | - | ||
1797 | - /** | ||
1798 | - * Processes next element of a group chain. Assumption is that if this | ||
1799 | - * group points to another group, the latter has already been created | ||
1800 | - * and this driver has received notification for it. A second assumption is | ||
1801 | - * that if there is another group waiting for this group then the appropriate | ||
1802 | - * stores already have the information to act upon the notification for the | ||
1803 | - * creation of this group. | ||
1804 | - * <p> | ||
1805 | - * The processing of the GroupChainElement depends on the number of groups | ||
1806 | - * this element is waiting on. For all group types other than SIMPLE, a | ||
1807 | - * GroupChainElement could be waiting on multiple groups. | ||
1808 | - * | ||
1809 | - * @param gce the group chain element to be processed next | ||
1810 | - */ | ||
1811 | - private void processGroupChain(GroupChainElem gce) { | ||
1812 | - int waitOnGroups = gce.decrementAndGetGroupsWaitedOn(); | ||
1813 | - if (waitOnGroups != 0) { | ||
1814 | - log.debug("GCE: {} not ready to be processed", gce); | ||
1815 | - return; | ||
1816 | - } | ||
1817 | - log.debug("GCE: {} ready to be processed", gce); | ||
1818 | - if (gce.addBucketToGroup) { | ||
1819 | - groupService.addBucketsToGroup(gce.groupDescription.deviceId(), | ||
1820 | - gce.groupDescription.appCookie(), | ||
1821 | - gce.groupDescription.buckets(), | ||
1822 | - gce.groupDescription.appCookie(), | ||
1823 | - gce.groupDescription.appId()); | ||
1824 | - } else { | ||
1825 | - groupService.addGroup(gce.groupDescription); | ||
1826 | - } | ||
1827 | - } | ||
1828 | - | ||
1829 | - private class GroupChecker implements Runnable { | ||
1830 | - @Override | ||
1831 | - public void run() { | ||
1832 | - Set<GroupKey> keys = pendingGroups.keySet().stream() | ||
1833 | - .filter(key -> groupService.getGroup(deviceId, key) != null) | ||
1834 | - .collect(Collectors.toSet()); | ||
1835 | - Set<GroupKey> otherkeys = pendingNextObjectives.asMap().keySet().stream() | ||
1836 | - .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null) | ||
1837 | - .collect(Collectors.toSet()); | ||
1838 | - keys.addAll(otherkeys); | ||
1839 | - | ||
1840 | - keys.stream().forEach(key -> | ||
1841 | - processPendingGroupsOrNextObjectives(key, false)); | ||
1842 | - } | ||
1843 | - } | ||
1844 | - | ||
1845 | - private void processPendingGroupsOrNextObjectives(GroupKey key, boolean added) { | ||
1846 | - //first check for group chain | ||
1847 | - Set<GroupChainElem> gceSet = pendingGroups.remove(key); | ||
1848 | - if (gceSet != null) { | ||
1849 | - for (GroupChainElem gce : gceSet) { | ||
1850 | - log.info("Group service {} group key {} in device {}. " | ||
1851 | - + "Processing next group in group chain with group id {}", | ||
1852 | - (added) ? "ADDED" : "processed", | ||
1853 | - key, deviceId, | ||
1854 | - Integer.toHexString(gce.groupDescription.givenGroupId())); | ||
1855 | - processGroupChain(gce); | ||
1856 | - } | ||
1857 | - } else { | ||
1858 | - // otherwise chain complete - check for waiting nextObjectives | ||
1859 | - List<OfdpaNextGroup> nextGrpList = pendingNextObjectives.getIfPresent(key); | ||
1860 | - if (nextGrpList != null) { | ||
1861 | - pendingNextObjectives.invalidate(key); | ||
1862 | - nextGrpList.forEach(nextGrp -> { | ||
1863 | - log.info("Group service {} group key {} in device:{}. " | ||
1864 | - + "Done implementing next objective: {} <<-->> gid:{}", | ||
1865 | - (added) ? "ADDED" : "processed", | ||
1866 | - key, deviceId, nextGrp.nextObjective().id(), | ||
1867 | - Integer.toHexString(groupService.getGroup(deviceId, key) | ||
1868 | - .givenGroupId())); | ||
1869 | - pass(nextGrp.nextObjective()); | ||
1870 | - flowObjectiveStore.putNextGroup(nextGrp.nextObjective().id(), nextGrp); | ||
1871 | - // check if addBuckets waiting for this completion | ||
1872 | - NextObjective pendBkt = pendingBuckets | ||
1873 | - .remove(nextGrp.nextObjective().id()); | ||
1874 | - if (pendBkt != null) { | ||
1875 | - addBucketToGroup(pendBkt, nextGrp); | ||
1876 | - } | ||
1877 | - }); | ||
1878 | - } | ||
1879 | - } | ||
1880 | - } | ||
1881 | - | ||
1882 | - private class InnerGroupListener implements GroupListener { | ||
1883 | - @Override | ||
1884 | - public void event(GroupEvent event) { | ||
1885 | - log.trace("received group event of type {}", event.type()); | ||
1886 | - if (event.type() == GroupEvent.Type.GROUP_ADDED) { | ||
1887 | - GroupKey key = event.subject().appCookie(); | ||
1888 | - processPendingGroupsOrNextObjectives(key, true); | ||
1889 | - } | ||
1890 | - } | ||
1891 | - } | ||
1892 | - | ||
1893 | - /** | ||
1894 | - * Represents an entire group-chain that implements a Next-Objective from | ||
1895 | - * the application. The objective is represented as a list of deques, where | ||
1896 | - * each deque is a separate chain of groups. | ||
1897 | - * <p> | ||
1898 | - * For example, an ECMP group with 3 buckets, where each bucket points to | ||
1899 | - * a group chain of L3 Unicast and L2 interface groups will look like this: | ||
1900 | - * <ul> | ||
1901 | - * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last) | ||
1902 | - * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last) | ||
1903 | - * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last) | ||
1904 | - * </ul> | ||
1905 | - * where the first element of each deque is the same, representing the | ||
1906 | - * top level ECMP group, while every other element represents a unique groupKey. | ||
1907 | - * <p> | ||
1908 | - * Also includes information about the next objective that | ||
1909 | - * resulted in this group-chain. | ||
1910 | - * | ||
1911 | - */ | ||
1912 | - private class OfdpaNextGroup implements NextGroup { | ||
1913 | - private final NextObjective nextObj; | ||
1914 | - private final List<Deque<GroupKey>> gkeys; | ||
1915 | - | ||
1916 | - public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) { | ||
1917 | - this.gkeys = gkeys; | ||
1918 | - this.nextObj = nextObj; | ||
1919 | - } | ||
1920 | - | ||
1921 | - @SuppressWarnings("unused") | ||
1922 | - public List<Deque<GroupKey>> groupKey() { | ||
1923 | - return gkeys; | ||
1924 | - } | ||
1925 | - | ||
1926 | - public NextObjective nextObjective() { | ||
1927 | - return nextObj; | ||
1928 | - } | ||
1929 | - | ||
1930 | - @Override | ||
1931 | - public byte[] data() { | ||
1932 | - return appKryo.serialize(gkeys); | ||
1933 | - } | ||
1934 | - | ||
1935 | - } | ||
1936 | - | ||
1937 | - /** | ||
1938 | - * Represents a group element that is part of a chain of groups. | ||
1939 | - * Stores enough information to create a Group Description to add the group | ||
1940 | - * to the switch by requesting the Group Service. Objects instantiating this | ||
1941 | - * class are meant to be temporary and live as long as it is needed to wait for | ||
1942 | - * preceding groups in the group chain to be created. | ||
1943 | - */ | ||
1944 | - private class GroupChainElem { | ||
1945 | - private GroupDescription groupDescription; | ||
1946 | - private AtomicInteger waitOnGroups; | ||
1947 | - private boolean addBucketToGroup; | ||
1948 | - | ||
1949 | - GroupChainElem(GroupDescription groupDescription, int waitOnGroups, | ||
1950 | - boolean addBucketToGroup) { | ||
1951 | - this.groupDescription = groupDescription; | ||
1952 | - this.waitOnGroups = new AtomicInteger(waitOnGroups); | ||
1953 | - this.addBucketToGroup = addBucketToGroup; | ||
1954 | - } | ||
1955 | - | ||
1956 | - /** | ||
1957 | - * This methods atomically decrements the counter for the number of | ||
1958 | - * groups this GroupChainElement is waiting on, for notifications from | ||
1959 | - * the Group Service. When this method returns a value of 0, this | ||
1960 | - * GroupChainElement is ready to be processed. | ||
1961 | - * | ||
1962 | - * @return integer indication of the number of notifications being waited on | ||
1963 | - */ | ||
1964 | - int decrementAndGetGroupsWaitedOn() { | ||
1965 | - return waitOnGroups.decrementAndGet(); | ||
1966 | - } | ||
1967 | - | ||
1968 | - @Override | ||
1969 | - public String toString() { | ||
1970 | - return (Integer.toHexString(groupDescription.givenGroupId()) + | ||
1971 | - " groupKey: " + groupDescription.appCookie() + | ||
1972 | - " waiting-on-groups: " + waitOnGroups.get() + | ||
1973 | - " addBucketToGroup: " + addBucketToGroup + | ||
1974 | - " device: " + deviceId); | ||
1975 | - } | ||
1976 | - } | ||
1977 | - | ||
1978 | } | 862 | } | ... | ... |
... | @@ -51,6 +51,7 @@ import org.onosproject.net.flow.criteria.Criterion.Type; | ... | @@ -51,6 +51,7 @@ import org.onosproject.net.flow.criteria.Criterion.Type; |
51 | import org.onosproject.net.flow.criteria.EthCriterion; | 51 | import org.onosproject.net.flow.criteria.EthCriterion; |
52 | import org.onosproject.net.flow.criteria.EthTypeCriterion; | 52 | import org.onosproject.net.flow.criteria.EthTypeCriterion; |
53 | import org.onosproject.net.flow.criteria.IPCriterion; | 53 | import org.onosproject.net.flow.criteria.IPCriterion; |
54 | +import org.onosproject.net.flow.criteria.MplsBosCriterion; | ||
54 | import org.onosproject.net.flow.criteria.MplsCriterion; | 55 | import org.onosproject.net.flow.criteria.MplsCriterion; |
55 | import org.onosproject.net.flow.criteria.PortCriterion; | 56 | import org.onosproject.net.flow.criteria.PortCriterion; |
56 | import org.onosproject.net.flow.criteria.VlanIdCriterion; | 57 | import org.onosproject.net.flow.criteria.VlanIdCriterion; |
... | @@ -593,9 +594,10 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour | ... | @@ -593,9 +594,10 @@ public class SpringOpenTTP extends AbstractHandlerBehaviour |
593 | .matchEthType(Ethernet.MPLS_UNICAST) | 594 | .matchEthType(Ethernet.MPLS_UNICAST) |
594 | .matchMplsLabel(((MplsCriterion) | 595 | .matchMplsLabel(((MplsCriterion) |
595 | selector.getCriterion(Criterion.Type.MPLS_LABEL)).label()); | 596 | selector.getCriterion(Criterion.Type.MPLS_LABEL)).label()); |
596 | - //TODO: Add Match for BoS | 597 | + if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) { |
597 | - //if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) { | 598 | + filteredSelectorBuilder.matchMplsBos(((MplsBosCriterion) |
598 | - //} | 599 | + selector.getCriterion(Type.MPLS_BOS)).mplsBos()); |
600 | + } | ||
599 | forTableId = mplsTableId; | 601 | forTableId = mplsTableId; |
600 | log.debug("processing MPLS specific forwarding objective:{} in dev:{}", | 602 | log.debug("processing MPLS specific forwarding objective:{} in dev:{}", |
601 | fwd.id(), deviceId); | 603 | fwd.id(), deviceId); | ... | ... |
... | @@ -34,6 +34,7 @@ import org.onosproject.net.flow.criteria.Criterion; | ... | @@ -34,6 +34,7 @@ import org.onosproject.net.flow.criteria.Criterion; |
34 | import org.onosproject.net.flow.criteria.EthCriterion; | 34 | import org.onosproject.net.flow.criteria.EthCriterion; |
35 | import org.onosproject.net.flow.criteria.EthTypeCriterion; | 35 | import org.onosproject.net.flow.criteria.EthTypeCriterion; |
36 | import org.onosproject.net.flow.criteria.IPCriterion; | 36 | import org.onosproject.net.flow.criteria.IPCriterion; |
37 | +import org.onosproject.net.flow.criteria.MplsBosCriterion; | ||
37 | import org.onosproject.net.flow.criteria.MplsCriterion; | 38 | import org.onosproject.net.flow.criteria.MplsCriterion; |
38 | import org.onosproject.net.flow.criteria.VlanIdCriterion; | 39 | import org.onosproject.net.flow.criteria.VlanIdCriterion; |
39 | import org.onosproject.net.flow.instructions.Instruction; | 40 | import org.onosproject.net.flow.instructions.Instruction; |
... | @@ -116,9 +117,10 @@ public class SpringOpenTTPDell extends SpringOpenTTP { | ... | @@ -116,9 +117,10 @@ public class SpringOpenTTPDell extends SpringOpenTTP { |
116 | .matchEthType(Ethernet.MPLS_UNICAST) | 117 | .matchEthType(Ethernet.MPLS_UNICAST) |
117 | .matchMplsLabel(((MplsCriterion) | 118 | .matchMplsLabel(((MplsCriterion) |
118 | selector.getCriterion(Criterion.Type.MPLS_LABEL)).label()); | 119 | selector.getCriterion(Criterion.Type.MPLS_LABEL)).label()); |
119 | - //TODO: Add Match for BoS | 120 | + if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) { |
120 | - //if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) { | 121 | + filteredSelectorBuilder.matchMplsBos(((MplsBosCriterion) |
121 | - //} | 122 | + selector.getCriterion(Criterion.Type.MPLS_BOS)).mplsBos()); |
123 | + } | ||
122 | forTableId = mplsTableId; | 124 | forTableId = mplsTableId; |
123 | log.debug("processing MPLS specific forwarding objective"); | 125 | log.debug("processing MPLS specific forwarding objective"); |
124 | } | 126 | } | ... | ... |
-
Please register or login to post a comment