Charles Chan

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
...@@ -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 }
......