blob: 2a4de9889692d2cff31cfc7b5e587d0fb53d4951 [file] [log] [blame]
Brian O'Connor7cbbbb72016-04-09 02:13:23 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Charles Chan188ebf52015-12-23 00:15:11 -080016package org.onosproject.driver.pipeline;
17
18import com.google.common.cache.Cache;
19import com.google.common.cache.CacheBuilder;
20import com.google.common.cache.RemovalCause;
21import com.google.common.cache.RemovalNotification;
Charles Chan5b9df8d2016-03-28 22:21:40 -070022import com.google.common.collect.ImmutableList;
23import com.google.common.collect.Lists;
Charles Chan188ebf52015-12-23 00:15:11 -080024import org.onlab.osgi.ServiceDirectory;
Charles Chan5b9df8d2016-03-28 22:21:40 -070025import org.onlab.packet.IpPrefix;
Charles Chan5270ed02016-01-30 23:22:37 -080026import org.onlab.packet.MacAddress;
Charles Chan188ebf52015-12-23 00:15:11 -080027import org.onlab.packet.MplsLabel;
28import org.onlab.packet.VlanId;
29import org.onosproject.core.ApplicationId;
30import org.onosproject.core.DefaultGroupId;
Charles Chan32562522016-04-07 14:37:14 -070031import org.onosproject.driver.extensions.OfdpaSetVlanVid;
Charles Chan188ebf52015-12-23 00:15:11 -080032import org.onosproject.net.DeviceId;
33import org.onosproject.net.PortNumber;
34import org.onosproject.net.behaviour.NextGroup;
35import org.onosproject.net.behaviour.PipelinerContext;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.TrafficSelector;
38import org.onosproject.net.flow.TrafficTreatment;
39import org.onosproject.net.flow.criteria.Criterion;
40import org.onosproject.net.flow.criteria.VlanIdCriterion;
41import org.onosproject.net.flow.instructions.Instruction;
42import org.onosproject.net.flow.instructions.Instructions;
43import org.onosproject.net.flow.instructions.L2ModificationInstruction;
44import org.onosproject.net.flowobjective.FlowObjectiveStore;
45import org.onosproject.net.flowobjective.NextObjective;
46import org.onosproject.net.flowobjective.ObjectiveError;
47import org.onosproject.net.group.DefaultGroupBucket;
48import org.onosproject.net.group.DefaultGroupDescription;
49import org.onosproject.net.group.DefaultGroupKey;
50import org.onosproject.net.group.Group;
51import org.onosproject.net.group.GroupBucket;
52import org.onosproject.net.group.GroupBuckets;
53import org.onosproject.net.group.GroupDescription;
54import org.onosproject.net.group.GroupEvent;
55import org.onosproject.net.group.GroupKey;
56import org.onosproject.net.group.GroupListener;
57import org.onosproject.net.group.GroupService;
Saurav Das8be4e3a2016-03-11 17:19:07 -080058import org.onosproject.store.service.AtomicCounter;
59import org.onosproject.store.service.StorageService;
Charles Chan188ebf52015-12-23 00:15:11 -080060import org.slf4j.Logger;
61
62import java.util.ArrayDeque;
63import java.util.ArrayList;
64import java.util.Collection;
65import java.util.Collections;
66import java.util.Deque;
Saurav Das1ce0a7b2016-10-21 14:06:29 -070067import java.util.HashSet;
Charles Chan188ebf52015-12-23 00:15:11 -080068import java.util.List;
Charles Chand0fd5dc2016-02-16 23:14:49 -080069import java.util.Objects;
Charles Chan188ebf52015-12-23 00:15:11 -080070import java.util.Set;
71import java.util.concurrent.ConcurrentHashMap;
72import java.util.concurrent.CopyOnWriteArrayList;
73import java.util.concurrent.Executors;
74import java.util.concurrent.ScheduledExecutorService;
75import java.util.concurrent.TimeUnit;
76import java.util.concurrent.atomic.AtomicInteger;
77import java.util.stream.Collectors;
78
79import static org.onlab.util.Tools.groupedThreads;
Pier Ventre140a8942016-11-02 07:26:38 -070080import static org.onosproject.driver.pipeline.Ofdpa2GroupHandler.OfdpaMplsGroupSubType.OFDPA_GROUP_TYPE_SHIFT;
81import static org.onosproject.driver.pipeline.Ofdpa2GroupHandler.OfdpaMplsGroupSubType.OFDPA_MPLS_SUBTYPE_SHIFT;
82import static org.onosproject.driver.pipeline.Ofdpa2Pipeline.isNotMplsBos;
83import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
84import static org.onosproject.net.flowobjective.NextObjective.Type.HASHED;
Charles Chan188ebf52015-12-23 00:15:11 -080085import static org.slf4j.LoggerFactory.getLogger;
86
87/**
Charles Chan40132b32017-01-22 00:19:37 -080088 * Group handler that emulates Broadcom OF-DPA TTP on CpqD.
Charles Chan188ebf52015-12-23 00:15:11 -080089 */
Charles Chan361154b2016-03-24 10:23:39 -070090public class Ofdpa2GroupHandler {
Charles Chan188ebf52015-12-23 00:15:11 -080091 /*
92 * OFDPA requires group-id's to have a certain form.
93 * L2 Interface Groups have <4bits-0><12bits-vlanid><16bits-portid>
94 * L3 Unicast Groups have <4bits-2><28bits-index>
95 * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index>
96 * L3 ECMP Groups have <4bits-7><28bits-index>
97 * L2 Flood Groups have <4bits-4><12bits-vlanid><16bits-index>
98 * L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
99 */
Charles Chan425854b2016-04-11 15:32:12 -0700100 protected static final int L2_INTERFACE_TYPE = 0x00000000;
101 protected static final int L3_INTERFACE_TYPE = 0x50000000;
102 protected static final int L3_UNICAST_TYPE = 0x20000000;
103 protected static final int L3_MULTICAST_TYPE = 0x60000000;
104 protected static final int MPLS_INTERFACE_TYPE = 0x90000000;
105 protected static final int MPLS_L3VPN_SUBTYPE = 0x92000000;
106 protected static final int L3_ECMP_TYPE = 0x70000000;
107 protected static final int L2_FLOOD_TYPE = 0x40000000;
Charles Chane849c192016-01-11 18:28:54 -0800108
Charles Chan425854b2016-04-11 15:32:12 -0700109 protected static final int TYPE_MASK = 0x0fffffff;
110 protected static final int SUBTYPE_MASK = 0x00ffffff;
111 protected static final int TYPE_VLAN_MASK = 0x0000ffff;
Charles Chane849c192016-01-11 18:28:54 -0800112
Charles Chan372b63e2017-02-07 12:10:53 -0800113 protected static final int THREE_BIT_MASK = 0x0fff;
114 protected static final int FOUR_BIT_MASK = 0xffff;
115 protected static final int PORT_LEN = 16;
116
Charles Chan425854b2016-04-11 15:32:12 -0700117 protected static final int PORT_LOWER_BITS_MASK = 0x3f;
118 protected static final long PORT_HIGHER_BITS_MASK = ~PORT_LOWER_BITS_MASK;
Charles Chan188ebf52015-12-23 00:15:11 -0800119
120 private final Logger log = getLogger(getClass());
121 private ServiceDirectory serviceDirectory;
122 protected GroupService groupService;
Saurav Das8be4e3a2016-03-11 17:19:07 -0800123 protected StorageService storageService;
Charles Chan188ebf52015-12-23 00:15:11 -0800124
Charles Chan425854b2016-04-11 15:32:12 -0700125 protected DeviceId deviceId;
Charles Chan188ebf52015-12-23 00:15:11 -0800126 private FlowObjectiveStore flowObjectiveStore;
Charles Chanfc5c7802016-05-17 13:13:55 -0700127 private Cache<GroupKey, List<OfdpaNextGroup>> pendingAddNextObjectives;
128 private Cache<NextObjective, List<GroupKey>> pendingRemoveNextObjectives;
Charles Chan188ebf52015-12-23 00:15:11 -0800129 private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups;
130 private ScheduledExecutorService groupChecker =
HIGUCHI Yutad9e01052016-04-14 09:31:42 -0700131 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner", "ofdpa2-%d", log));
Charles Chan188ebf52015-12-23 00:15:11 -0800132
133 // index number for group creation
Saurav Das8be4e3a2016-03-11 17:19:07 -0800134 private AtomicCounter nextIndex;
Charles Chan188ebf52015-12-23 00:15:11 -0800135
Charles Chan188ebf52015-12-23 00:15:11 -0800136 // local store for pending bucketAdds - by design there can only be one
137 // pending bucket for a group
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700138 protected ConcurrentHashMap<Integer, NextObjective> pendingBuckets =
139 new ConcurrentHashMap<>();
Charles Chan188ebf52015-12-23 00:15:11 -0800140
Charles Chan40132b32017-01-22 00:19:37 -0800141 /**
142 * Determines whether this pipeline support copy ttl instructions or not.
143 *
144 * @return true if copy ttl instructions are supported
145 */
146 protected boolean supportCopyTtl() {
147 return true;
148 }
149
150 /**
151 * Determines whether this pipeline support set mpls bos instruction or not.
152 *
153 * @return true if set mpls bos instruction is supported
154 */
155 protected boolean supportSetMplsBos() {
156 return true;
157 }
158
Charles Chan188ebf52015-12-23 00:15:11 -0800159 protected void init(DeviceId deviceId, PipelinerContext context) {
160 this.deviceId = deviceId;
161 this.flowObjectiveStore = context.store();
162 this.serviceDirectory = context.directory();
163 this.groupService = serviceDirectory.get(GroupService.class);
Saurav Das8be4e3a2016-03-11 17:19:07 -0800164 this.storageService = serviceDirectory.get(StorageService.class);
Madan Jampanid5714e02016-04-19 14:15:20 -0700165 this.nextIndex = storageService.getAtomicCounter("group-id-index-counter");
Charles Chan188ebf52015-12-23 00:15:11 -0800166
Charles Chanfc5c7802016-05-17 13:13:55 -0700167 pendingAddNextObjectives = CacheBuilder.newBuilder()
Charles Chan188ebf52015-12-23 00:15:11 -0800168 .expireAfterWrite(20, TimeUnit.SECONDS)
169 .removalListener((
170 RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> {
171 if (notification.getCause() == RemovalCause.EXPIRED) {
172 notification.getValue().forEach(ofdpaNextGrp ->
Charles Chan361154b2016-03-24 10:23:39 -0700173 Ofdpa2Pipeline.fail(ofdpaNextGrp.nextObj,
Charles Chan188ebf52015-12-23 00:15:11 -0800174 ObjectiveError.GROUPINSTALLATIONFAILED));
Charles Chanfc5c7802016-05-17 13:13:55 -0700175 }
176 }).build();
Charles Chan188ebf52015-12-23 00:15:11 -0800177
Charles Chanfc5c7802016-05-17 13:13:55 -0700178 pendingRemoveNextObjectives = CacheBuilder.newBuilder()
179 .expireAfterWrite(20, TimeUnit.SECONDS)
180 .removalListener((
181 RemovalNotification<NextObjective, List<GroupKey>> notification) -> {
182 if (notification.getCause() == RemovalCause.EXPIRED) {
183 Ofdpa2Pipeline.fail(notification.getKey(),
184 ObjectiveError.GROUPREMOVALFAILED);
Charles Chan188ebf52015-12-23 00:15:11 -0800185 }
186 }).build();
187 pendingGroups = new ConcurrentHashMap<>();
188 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500, TimeUnit.MILLISECONDS);
189
190 groupService.addListener(new InnerGroupListener());
191 }
192
Pier Ventre140a8942016-11-02 07:26:38 -0700193 /**
194 * The purpose of this function is to verify if the hashed next
195 * objective is supported by the current pipeline.
196 *
197 * @param nextObjective the hashed objective to verify
198 * @return true if the hashed objective is supported. Otherwise false.
199 */
200 public boolean verifyHashedNextObjective(NextObjective nextObjective) {
201 // if it is not hashed, there is something wrong;
202 if (nextObjective.type() != HASHED) {
203 return false;
204 }
205 // The case non supported is the MPLS-ECMP. For now, we try
206 // to create a MPLS-ECMP for the transport of a VPWS. The
207 // necessary info are contained in the meta selector. In particular
208 // we are looking for the case of BoS==False;
209 TrafficSelector metaSelector = nextObjective.meta();
210 if (metaSelector != null && isNotMplsBos(metaSelector)) {
211 return false;
212 }
213
214 return true;
215 }
216
Saurav Das8be4e3a2016-03-11 17:19:07 -0800217 //////////////////////////////////////
218 // Group Creation
219 //////////////////////////////////////
220
Charles Chan188ebf52015-12-23 00:15:11 -0800221 protected void addGroup(NextObjective nextObjective) {
222 switch (nextObjective.type()) {
223 case SIMPLE:
224 Collection<TrafficTreatment> treatments = nextObjective.next();
225 if (treatments.size() != 1) {
226 log.error("Next Objectives of type Simple should only have a "
227 + "single Traffic Treatment. Next Objective Id:{}",
228 nextObjective.id());
Charles Chan361154b2016-03-24 10:23:39 -0700229 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -0800230 return;
231 }
232 processSimpleNextObjective(nextObjective);
233 break;
234 case BROADCAST:
235 processBroadcastNextObjective(nextObjective);
236 break;
237 case HASHED:
Pier Ventre140a8942016-11-02 07:26:38 -0700238 if (!verifyHashedNextObjective(nextObjective)) {
239 log.error("Next Objectives of type hashed not supported. Next Objective Id:{}",
240 nextObjective.id());
241 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.BADPARAMS);
242 return;
243 }
Charles Chan188ebf52015-12-23 00:15:11 -0800244 processHashedNextObjective(nextObjective);
245 break;
246 case FAILOVER:
Charles Chan361154b2016-03-24 10:23:39 -0700247 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.UNSUPPORTED);
Charles Chan188ebf52015-12-23 00:15:11 -0800248 log.warn("Unsupported next objective type {}", nextObjective.type());
249 break;
250 default:
Charles Chan361154b2016-03-24 10:23:39 -0700251 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.UNKNOWN);
Charles Chan188ebf52015-12-23 00:15:11 -0800252 log.warn("Unknown next objective type {}", nextObjective.type());
253 }
254 }
255
256 /**
257 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
258 * a chain of groups. The simple Next Objective passed
259 * in by the application has to be broken up into a group chain
260 * comprising of an L3 Unicast Group that points to an L2 Interface
261 * Group which in-turn points to an output port. In some cases, the simple
262 * next Objective can just be an L2 interface without the need for chaining.
263 *
264 * @param nextObj the nextObjective of type SIMPLE
265 */
266 private void processSimpleNextObjective(NextObjective nextObj) {
267 TrafficTreatment treatment = nextObj.next().iterator().next();
268 // determine if plain L2 or L3->L2
269 boolean plainL2 = true;
270 for (Instruction ins : treatment.allInstructions()) {
271 if (ins.type() == Instruction.Type.L2MODIFICATION) {
272 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
273 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_DST ||
274 l2ins.subtype() == L2ModificationInstruction.L2SubType.ETH_SRC) {
275 plainL2 = false;
276 break;
277 }
278 }
279 }
280
281 if (plainL2) {
282 createL2InterfaceGroup(nextObj);
283 return;
284 }
285
Pier Ventre140a8942016-11-02 07:26:38 -0700286 boolean isMpls = false;
287 if (nextObj.meta() != null) {
288 isMpls = isNotMplsBos(nextObj.meta());
289 }
290
Charles Chan188ebf52015-12-23 00:15:11 -0800291 // break up simple next objective to GroupChain objects
292 GroupInfo groupInfo = createL2L3Chain(treatment, nextObj.id(),
Pier Ventre140a8942016-11-02 07:26:38 -0700293 nextObj.appId(), isMpls,
Saurav Das8be4e3a2016-03-11 17:19:07 -0800294 nextObj.meta());
Charles Chan188ebf52015-12-23 00:15:11 -0800295 if (groupInfo == null) {
296 log.error("Could not process nextObj={} in dev:{}", nextObj.id(), deviceId);
297 return;
298 }
299 // create object for local and distributed storage
300 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700301 gkeyChain.addFirst(groupInfo.innerMostGroupDesc.appCookie());
302 gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700303 OfdpaNextGroup ofdpaGrp =
304 new OfdpaNextGroup(Collections.singletonList(gkeyChain), nextObj);
Charles Chan188ebf52015-12-23 00:15:11 -0800305
306 // store l3groupkey with the ofdpaNextGroup for the nextObjective that depends on it
Charles Chan5b9df8d2016-03-28 22:21:40 -0700307 updatePendingNextObjective(groupInfo.nextGroupDesc.appCookie(), ofdpaGrp);
Charles Chan188ebf52015-12-23 00:15:11 -0800308
309 // now we are ready to send the l2 groupDescription (inner), as all the stores
310 // that will get async replies have been updated. By waiting to update
311 // the stores, we prevent nasty race conditions.
Charles Chan5b9df8d2016-03-28 22:21:40 -0700312 groupService.addGroup(groupInfo.innerMostGroupDesc);
Charles Chan188ebf52015-12-23 00:15:11 -0800313 }
314
Charles Chan188ebf52015-12-23 00:15:11 -0800315 /**
316 * Creates a simple L2 Interface Group.
317 *
318 * @param nextObj the next Objective
319 */
320 private void createL2InterfaceGroup(NextObjective nextObj) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700321 VlanId assignedVlan = Ofdpa2Pipeline.readVlanFromSelector(nextObj.meta());
322 if (assignedVlan == null) {
323 log.warn("VLAN ID required by simple next obj is missing. Abort.");
324 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -0800325 return;
326 }
327
Charles Chan5b9df8d2016-03-28 22:21:40 -0700328 List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
Charles Chan188ebf52015-12-23 00:15:11 -0800329
Charles Chan5b9df8d2016-03-28 22:21:40 -0700330 // There is only one L2 interface group in this case
331 GroupDescription l2InterfaceGroupDesc = groupInfos.get(0).innerMostGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -0800332
Charles Chan5b9df8d2016-03-28 22:21:40 -0700333 // Put all dependency information into allGroupKeys
334 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
335 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
336 gkeyChain.addFirst(l2InterfaceGroupDesc.appCookie());
337 allGroupKeys.add(gkeyChain);
Charles Chan188ebf52015-12-23 00:15:11 -0800338
Charles Chan5b9df8d2016-03-28 22:21:40 -0700339 // Point the next objective to this group
340 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
341 updatePendingNextObjective(l2InterfaceGroupDesc.appCookie(), ofdpaGrp);
342
343 // Start installing the inner-most group
344 groupService.addGroup(l2InterfaceGroupDesc);
Charles Chan188ebf52015-12-23 00:15:11 -0800345 }
346
347 /**
348 * Creates one of two possible group-chains from the treatment
349 * passed in. Depending on the MPLS boolean, this method either creates
Charles Chan425854b2016-04-11 15:32:12 -0700350 * an L3Unicast Group --&gt; L2Interface Group, if mpls is false;
351 * or MPLSInterface Group --&gt; L2Interface Group, if mpls is true;
Charles Chan188ebf52015-12-23 00:15:11 -0800352 * The returned 'inner' group description is always the L2 Interface group.
353 *
354 * @param treatment that needs to be broken up to create the group chain
355 * @param nextId of the next objective that needs this group chain
356 * @param appId of the application that sent this next objective
357 * @param mpls determines if L3Unicast or MPLSInterface group is created
358 * @param meta metadata passed in by the application as part of the nextObjective
359 * @return GroupInfo containing the GroupDescription of the
360 * L2Interface group(inner) and the GroupDescription of the (outer)
361 * L3Unicast/MPLSInterface group. May return null if there is an
362 * error in processing the chain
363 */
Charles Chan425854b2016-04-11 15:32:12 -0700364 protected GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
Charles Chanf9e98652016-09-07 16:54:23 -0700365 ApplicationId appId, boolean mpls,
366 TrafficSelector meta) {
367 return createL2L3ChainInternal(treatment, nextId, appId, mpls, meta, true);
368 }
369
370 /**
371 * Internal implementation of createL2L3Chain.
372 * <p>
373 * The is_present bit in set_vlan_vid action is required to be 0 in OFDPA i12.
374 * Since it is non-OF spec, we need an extension treatment for that.
375 * The useSetVlanExtension must be set to false for OFDPA i12.
376 * </p>
377 *
378 * @param treatment that needs to be broken up to create the group chain
379 * @param nextId of the next objective that needs this group chain
380 * @param appId of the application that sent this next objective
381 * @param mpls determines if L3Unicast or MPLSInterface group is created
382 * @param meta metadata passed in by the application as part of the nextObjective
383 * @param useSetVlanExtension use the setVlanVid extension that has is_present bit set to 0.
384 * @return GroupInfo containing the GroupDescription of the
385 * L2Interface group(inner) and the GroupDescription of the (outer)
386 * L3Unicast/MPLSInterface group. May return null if there is an
387 * error in processing the chain
388 */
389 protected GroupInfo createL2L3ChainInternal(TrafficTreatment treatment, int nextId,
Saurav Das8be4e3a2016-03-11 17:19:07 -0800390 ApplicationId appId, boolean mpls,
Charles Chanf9e98652016-09-07 16:54:23 -0700391 TrafficSelector meta, boolean useSetVlanExtension) {
Charles Chan188ebf52015-12-23 00:15:11 -0800392 // for the l2interface group, get vlan and port info
393 // for the outer group, get the src/dst mac, and vlan info
394 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
395 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
396 VlanId vlanid = null;
397 long portNum = 0;
398 boolean setVlan = false, popVlan = false;
Charles Chand0fd5dc2016-02-16 23:14:49 -0800399 MacAddress srcMac = MacAddress.ZERO;
Charles Chan5270ed02016-01-30 23:22:37 -0800400 MacAddress dstMac = MacAddress.ZERO;
Charles Chan188ebf52015-12-23 00:15:11 -0800401 for (Instruction ins : treatment.allInstructions()) {
402 if (ins.type() == Instruction.Type.L2MODIFICATION) {
403 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
404 switch (l2ins.subtype()) {
405 case ETH_DST:
Charles Chan5270ed02016-01-30 23:22:37 -0800406 dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
407 outerTtb.setEthDst(dstMac);
Charles Chan188ebf52015-12-23 00:15:11 -0800408 break;
409 case ETH_SRC:
Charles Chand0fd5dc2016-02-16 23:14:49 -0800410 srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
411 outerTtb.setEthSrc(srcMac);
Charles Chan188ebf52015-12-23 00:15:11 -0800412 break;
413 case VLAN_ID:
414 vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
Charles Chanf9e98652016-09-07 16:54:23 -0700415 if (useSetVlanExtension) {
416 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanid);
417 outerTtb.extension(ofdpaSetVlanVid, deviceId);
418 } else {
419 outerTtb.setVlanId(vlanid);
420 }
Charles Chan188ebf52015-12-23 00:15:11 -0800421 setVlan = true;
422 break;
423 case VLAN_POP:
424 innerTtb.popVlan();
425 popVlan = true;
426 break;
427 case DEC_MPLS_TTL:
428 case MPLS_LABEL:
429 case MPLS_POP:
430 case MPLS_PUSH:
431 case VLAN_PCP:
432 case VLAN_PUSH:
433 default:
434 break;
435 }
436 } else if (ins.type() == Instruction.Type.OUTPUT) {
437 portNum = ((Instructions.OutputInstruction) ins).port().toLong();
438 innerTtb.add(ins);
439 } else {
440 log.warn("Driver does not handle this type of TrafficTreatment"
441 + " instruction in nextObjectives: {}", ins.type());
442 }
443 }
444
445 if (vlanid == null && meta != null) {
446 // use metadata if available
Pier Ventre140a8942016-11-02 07:26:38 -0700447 Criterion vidCriterion = meta.getCriterion(VLAN_VID);
Charles Chan188ebf52015-12-23 00:15:11 -0800448 if (vidCriterion != null) {
449 vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
450 }
451 // if vlan is not set, use the vlan in metadata for outerTtb
452 if (vlanid != null && !setVlan) {
Charles Chanf9e98652016-09-07 16:54:23 -0700453 if (useSetVlanExtension) {
454 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(vlanid);
455 outerTtb.extension(ofdpaSetVlanVid, deviceId);
456 } else {
457 outerTtb.setVlanId(vlanid);
458 }
Charles Chan188ebf52015-12-23 00:15:11 -0800459 }
460 }
461
462 if (vlanid == null) {
463 log.error("Driver cannot process an L2/L3 group chain without "
464 + "egress vlan information for dev: {} port:{}",
465 deviceId, portNum);
466 return null;
467 }
468
469 if (!setVlan && !popVlan) {
470 // untagged outgoing port
471 TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder();
472 temp.popVlan();
473 innerTtb.build().allInstructions().forEach(i -> temp.add(i));
474 innerTtb = temp;
475 }
476
477 // assemble information for ofdpa l2interface group
Charles Chane849c192016-01-11 18:28:54 -0800478 int l2groupId = L2_INTERFACE_TYPE | (vlanid.toShort() << 16) | (int) portNum;
Saurav Das8be4e3a2016-03-11 17:19:07 -0800479 // a globally unique groupkey that is different for ports in the same device,
Charles Chan188ebf52015-12-23 00:15:11 -0800480 // but different for the same portnumber on different devices. Also different
481 // for the various group-types created out of the same next objective.
Charles Chane849c192016-01-11 18:28:54 -0800482 int l2gk = l2InterfaceGroupKey(deviceId, vlanid, portNum);
Charles Chan361154b2016-03-24 10:23:39 -0700483 final GroupKey l2groupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
Charles Chan188ebf52015-12-23 00:15:11 -0800484
485 // assemble information for outer group
486 GroupDescription outerGrpDesc = null;
487 if (mpls) {
488 // outer group is MPLSInteface
Saurav Das8be4e3a2016-03-11 17:19:07 -0800489 int mplsInterfaceIndex = getNextAvailableIndex();
490 int mplsgroupId = MPLS_INTERFACE_TYPE | (SUBTYPE_MASK & mplsInterfaceIndex);
491 final GroupKey mplsgroupkey = new DefaultGroupKey(
Charles Chan361154b2016-03-24 10:23:39 -0700492 Ofdpa2Pipeline.appKryo.serialize(mplsInterfaceIndex));
Charles Chan188ebf52015-12-23 00:15:11 -0800493 outerTtb.group(new DefaultGroupId(l2groupId));
494 // create the mpls-interface group description to wait for the
495 // l2 interface group to be processed
496 GroupBucket mplsinterfaceGroupBucket =
497 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
498 outerGrpDesc = new DefaultGroupDescription(
499 deviceId,
500 GroupDescription.Type.INDIRECT,
501 new GroupBuckets(Collections.singletonList(
502 mplsinterfaceGroupBucket)),
503 mplsgroupkey,
504 mplsgroupId,
505 appId);
506 log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
507 deviceId, Integer.toHexString(mplsgroupId),
508 mplsgroupkey, nextId);
509 } else {
510 // outer group is L3Unicast
Saurav Das8be4e3a2016-03-11 17:19:07 -0800511 int l3unicastIndex = getNextAvailableIndex();
512 int l3groupId = L3_UNICAST_TYPE | (TYPE_MASK & l3unicastIndex);
513 final GroupKey l3groupkey = new DefaultGroupKey(
Charles Chan361154b2016-03-24 10:23:39 -0700514 Ofdpa2Pipeline.appKryo.serialize(l3unicastIndex));
Charles Chan188ebf52015-12-23 00:15:11 -0800515 outerTtb.group(new DefaultGroupId(l2groupId));
516 // create the l3unicast group description to wait for the
517 // l2 interface group to be processed
518 GroupBucket l3unicastGroupBucket =
519 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
520 outerGrpDesc = new DefaultGroupDescription(
521 deviceId,
522 GroupDescription.Type.INDIRECT,
523 new GroupBuckets(Collections.singletonList(
524 l3unicastGroupBucket)),
525 l3groupkey,
526 l3groupId,
527 appId);
528 log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
529 deviceId, Integer.toHexString(l3groupId),
530 l3groupkey, nextId);
531 }
532
533 // store l2groupkey with the groupChainElem for the outer-group that depends on it
534 GroupChainElem gce = new GroupChainElem(outerGrpDesc, 1, false);
535 updatePendingGroups(l2groupkey, gce);
536
537 // create group description for the inner l2interfacegroup
Charles Chan5b9df8d2016-03-28 22:21:40 -0700538 GroupBucket l2InterfaceGroupBucket =
Charles Chan188ebf52015-12-23 00:15:11 -0800539 DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
540 GroupDescription l2groupDescription =
541 new DefaultGroupDescription(
542 deviceId,
543 GroupDescription.Type.INDIRECT,
544 new GroupBuckets(Collections.singletonList(
Charles Chan5b9df8d2016-03-28 22:21:40 -0700545 l2InterfaceGroupBucket)),
Charles Chan188ebf52015-12-23 00:15:11 -0800546 l2groupkey,
547 l2groupId,
548 appId);
549 log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
550 deviceId, Integer.toHexString(l2groupId),
551 l2groupkey, nextId);
552 return new GroupInfo(l2groupDescription, outerGrpDesc);
553
554 }
555
556 /**
557 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
558 * a chain of groups. The broadcast Next Objective passed in by the application
559 * has to be broken up into a group chain comprising of an
Saurav Das1a129a02016-11-18 15:21:57 -0800560 * L2 Flood group or L3 Multicast group, whose buckets point to L2 Interface groups.
Charles Chan188ebf52015-12-23 00:15:11 -0800561 *
562 * @param nextObj the nextObjective of type BROADCAST
563 */
564 private void processBroadcastNextObjective(NextObjective nextObj) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700565 VlanId assignedVlan = Ofdpa2Pipeline.readVlanFromSelector(nextObj.meta());
566 if (assignedVlan == null) {
567 log.warn("VLAN ID required by broadcast next obj is missing. Abort.");
568 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -0800569 return;
570 }
Charles Chan188ebf52015-12-23 00:15:11 -0800571
Charles Chan5b9df8d2016-03-28 22:21:40 -0700572 List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
573
574 IpPrefix ipDst = Ofdpa2Pipeline.readIpDstFromSelector(nextObj.meta());
575 if (ipDst != null) {
576 if (ipDst.isMulticast()) {
577 createL3MulticastGroup(nextObj, assignedVlan, groupInfos);
578 } else {
579 log.warn("Broadcast NextObj with non-multicast IP address {}", nextObj);
580 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
581 return;
582 }
583 } else {
584 createL2FloodGroup(nextObj, assignedVlan, groupInfos);
585 }
586 }
587
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700588 private List<GroupInfo> prepareL2InterfaceGroup(NextObjective nextObj,
589 VlanId assignedVlan) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700590 ImmutableList.Builder<GroupInfo> groupInfoBuilder = ImmutableList.builder();
591
592 // break up broadcast next objective to multiple groups
593 Collection<TrafficTreatment> buckets = nextObj.next();
594
Charles Chan188ebf52015-12-23 00:15:11 -0800595 // each treatment is converted to an L2 interface group
Charles Chan188ebf52015-12-23 00:15:11 -0800596 for (TrafficTreatment treatment : buckets) {
597 TrafficTreatment.Builder newTreatment = DefaultTrafficTreatment.builder();
598 PortNumber portNum = null;
Charles Chan5b9df8d2016-03-28 22:21:40 -0700599 VlanId egressVlan = null;
Charles Chan188ebf52015-12-23 00:15:11 -0800600 // ensure that the only allowed treatments are pop-vlan and output
601 for (Instruction ins : treatment.allInstructions()) {
602 if (ins.type() == Instruction.Type.L2MODIFICATION) {
603 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
604 switch (l2ins.subtype()) {
605 case VLAN_POP:
606 newTreatment.add(l2ins);
607 break;
Charles Chan5b9df8d2016-03-28 22:21:40 -0700608 case VLAN_ID:
609 egressVlan = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
610 break;
Charles Chan188ebf52015-12-23 00:15:11 -0800611 default:
612 log.debug("action {} not permitted for broadcast nextObj",
613 l2ins.subtype());
614 break;
615 }
616 } else if (ins.type() == Instruction.Type.OUTPUT) {
617 portNum = ((Instructions.OutputInstruction) ins).port();
618 newTreatment.add(ins);
619 } else {
Charles Chane849c192016-01-11 18:28:54 -0800620 log.debug("TrafficTreatment of type {} not permitted in " +
621 " broadcast nextObjective", ins.type());
Charles Chan188ebf52015-12-23 00:15:11 -0800622 }
623 }
624
Charles Chan188ebf52015-12-23 00:15:11 -0800625 // assemble info for l2 interface group
Charles Chan5b9df8d2016-03-28 22:21:40 -0700626 VlanId l2InterfaceGroupVlan =
627 (egressVlan != null && !assignedVlan.equals(egressVlan)) ?
628 egressVlan : assignedVlan;
629 int l2gk = l2InterfaceGroupKey(deviceId, l2InterfaceGroupVlan, portNum.toLong());
630 final GroupKey l2InterfaceGroupKey =
631 new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
Charles Chan372b63e2017-02-07 12:10:53 -0800632 int l2InterfaceGroupId = L2_INTERFACE_TYPE |
633 ((l2InterfaceGroupVlan.toShort() & THREE_BIT_MASK) << PORT_LEN) |
634 ((int) portNum.toLong() & FOUR_BIT_MASK);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700635 GroupBucket l2InterfaceGroupBucket =
Charles Chan188ebf52015-12-23 00:15:11 -0800636 DefaultGroupBucket.createIndirectGroupBucket(newTreatment.build());
Charles Chan5b9df8d2016-03-28 22:21:40 -0700637 GroupDescription l2InterfaceGroupDescription =
Charles Chan188ebf52015-12-23 00:15:11 -0800638 new DefaultGroupDescription(
639 deviceId,
640 GroupDescription.Type.INDIRECT,
641 new GroupBuckets(Collections.singletonList(
Charles Chan5b9df8d2016-03-28 22:21:40 -0700642 l2InterfaceGroupBucket)),
643 l2InterfaceGroupKey,
644 l2InterfaceGroupId,
Charles Chan188ebf52015-12-23 00:15:11 -0800645 nextObj.appId());
646 log.debug("Trying L2-Interface: device:{} gid:{} gkey:{} nextid:{}",
Charles Chan5b9df8d2016-03-28 22:21:40 -0700647 deviceId, Integer.toHexString(l2InterfaceGroupId),
648 l2InterfaceGroupKey, nextObj.id());
Charles Chan188ebf52015-12-23 00:15:11 -0800649
Charles Chan5b9df8d2016-03-28 22:21:40 -0700650 groupInfoBuilder.add(new GroupInfo(l2InterfaceGroupDescription,
651 l2InterfaceGroupDescription));
Charles Chan188ebf52015-12-23 00:15:11 -0800652 }
Charles Chan5b9df8d2016-03-28 22:21:40 -0700653 return groupInfoBuilder.build();
654 }
Charles Chan188ebf52015-12-23 00:15:11 -0800655
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700656 private void createL2FloodGroup(NextObjective nextObj, VlanId vlanId,
657 List<GroupInfo> groupInfos) {
658 // assemble info for l2 flood group. Since there can be only one flood
659 // group for a vlan, its index is always the same - 0
Saurav Das0fd79d92016-03-07 10:58:36 -0800660 Integer l2floodgroupId = L2_FLOOD_TYPE | (vlanId.toShort() << 16);
Saurav Das8be4e3a2016-03-11 17:19:07 -0800661 int l2floodgk = getNextAvailableIndex();
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700662 final GroupKey l2floodgroupkey =
663 new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2floodgk));
Charles Chan5b9df8d2016-03-28 22:21:40 -0700664
Charles Chan188ebf52015-12-23 00:15:11 -0800665 // collection of group buckets pointing to all the l2 interface groups
Charles Chan5b9df8d2016-03-28 22:21:40 -0700666 List<GroupBucket> l2floodBuckets = Lists.newArrayList();
667 groupInfos.forEach(groupInfo -> {
668 GroupDescription l2intGrpDesc = groupInfo.nextGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -0800669 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
670 ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId()));
671 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
672 l2floodBuckets.add(abucket);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700673 });
Charles Chan188ebf52015-12-23 00:15:11 -0800674 // create the l2flood group-description to wait for all the
675 // l2interface groups to be processed
676 GroupDescription l2floodGroupDescription =
677 new DefaultGroupDescription(
678 deviceId,
679 GroupDescription.Type.ALL,
680 new GroupBuckets(l2floodBuckets),
681 l2floodgroupkey,
682 l2floodgroupId,
683 nextObj.appId());
Charles Chan188ebf52015-12-23 00:15:11 -0800684 log.debug("Trying L2-Flood: device:{} gid:{} gkey:{} nextid:{}",
685 deviceId, Integer.toHexString(l2floodgroupId),
686 l2floodgroupkey, nextObj.id());
687
Charles Chan5b9df8d2016-03-28 22:21:40 -0700688 // Put all dependency information into allGroupKeys
689 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
690 groupInfos.forEach(groupInfo -> {
691 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
692 // In this case we should have L2 interface group only
693 gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
694 gkeyChain.addFirst(l2floodgroupkey);
695 allGroupKeys.add(gkeyChain);
696 });
Charles Chan188ebf52015-12-23 00:15:11 -0800697
Charles Chan5b9df8d2016-03-28 22:21:40 -0700698 // Point the next objective to this group
699 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
Charles Chan188ebf52015-12-23 00:15:11 -0800700 updatePendingNextObjective(l2floodgroupkey, ofdpaGrp);
701
Charles Chan5b9df8d2016-03-28 22:21:40 -0700702 GroupChainElem gce = new GroupChainElem(l2floodGroupDescription,
703 groupInfos.size(), false);
704 groupInfos.forEach(groupInfo -> {
705 // Point this group to the next group
706 updatePendingGroups(groupInfo.nextGroupDesc.appCookie(), gce);
707 // Start installing the inner-most group
708 groupService.addGroup(groupInfo.innerMostGroupDesc);
709 });
710 }
711
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700712 private void createL3MulticastGroup(NextObjective nextObj, VlanId vlanId,
713 List<GroupInfo> groupInfos) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700714 List<GroupBucket> l3McastBuckets = new ArrayList<>();
715 groupInfos.forEach(groupInfo -> {
716 // Points to L3 interface group if there is one.
717 // Otherwise points to L2 interface group directly.
718 GroupDescription nextGroupDesc = (groupInfo.nextGroupDesc != null) ?
719 groupInfo.nextGroupDesc : groupInfo.innerMostGroupDesc;
720 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
721 ttb.group(new DefaultGroupId(nextGroupDesc.givenGroupId()));
722 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
723 l3McastBuckets.add(abucket);
724 });
725
726 int l3MulticastIndex = getNextAvailableIndex();
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700727 int l3MulticastGroupId = L3_MULTICAST_TYPE |
728 vlanId.toShort() << 16 | (TYPE_VLAN_MASK & l3MulticastIndex);
729 final GroupKey l3MulticastGroupKey =
730 new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l3MulticastIndex));
Charles Chan5b9df8d2016-03-28 22:21:40 -0700731
732 GroupDescription l3MulticastGroupDesc = new DefaultGroupDescription(deviceId,
733 GroupDescription.Type.ALL,
734 new GroupBuckets(l3McastBuckets),
735 l3MulticastGroupKey,
736 l3MulticastGroupId,
737 nextObj.appId());
738
739 // Put all dependency information into allGroupKeys
740 List<Deque<GroupKey>> allGroupKeys = Lists.newArrayList();
741 groupInfos.forEach(groupInfo -> {
742 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
743 gkeyChain.addFirst(groupInfo.innerMostGroupDesc.appCookie());
744 // Add L3 interface group to the chain if there is one.
745 if (!groupInfo.nextGroupDesc.equals(groupInfo.innerMostGroupDesc)) {
746 gkeyChain.addFirst(groupInfo.nextGroupDesc.appCookie());
747 }
748 gkeyChain.addFirst(l3MulticastGroupKey);
749 allGroupKeys.add(gkeyChain);
750 });
751
752 // Point the next objective to this group
753 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
754 updatePendingNextObjective(l3MulticastGroupKey, ofdpaGrp);
755
756 GroupChainElem outerGce = new GroupChainElem(l3MulticastGroupDesc,
757 groupInfos.size(), false);
758 groupInfos.forEach(groupInfo -> {
759 // Point this group (L3 multicast) to the next group
760 updatePendingGroups(groupInfo.nextGroupDesc.appCookie(), outerGce);
761
762 // Point next group to inner-most group, if any
763 if (!groupInfo.nextGroupDesc.equals(groupInfo.innerMostGroupDesc)) {
764 GroupChainElem innerGce = new GroupChainElem(groupInfo.nextGroupDesc,
765 1, false);
766 updatePendingGroups(groupInfo.innerMostGroupDesc.appCookie(), innerGce);
767 }
768
769 // Start installing the inner-most group
770 groupService.addGroup(groupInfo.innerMostGroupDesc);
771 });
Charles Chan188ebf52015-12-23 00:15:11 -0800772 }
773
Charles Chan188ebf52015-12-23 00:15:11 -0800774 /**
775 * As per the OFDPA 2.0 TTP, packets are sent out of ports by using
776 * a chain of groups. The hashed Next Objective passed in by the application
777 * has to be broken up into a group chain comprising of an
778 * L3 ECMP group as the top level group. Buckets of this group can point
779 * to a variety of groups in a group chain, depending on the whether
780 * MPLS labels are being pushed or not.
781 * <p>
782 * NOTE: We do not create MPLS ECMP groups as they are unimplemented in
783 * OF-DPA 2.0 (even though it is in the spec). Therefore we do not
784 * check the nextObjective meta to see what is matching before being
785 * sent to this nextObjective.
786 *
787 * @param nextObj the nextObjective of type HASHED
788 */
Pier Ventre140a8942016-11-02 07:26:38 -0700789 protected void processHashedNextObjective(NextObjective nextObj) {
Charles Chan188ebf52015-12-23 00:15:11 -0800790 // storage for all group keys in the chain of groups created
791 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
792 List<GroupInfo> unsentGroups = new ArrayList<>();
793 createHashBucketChains(nextObj, allGroupKeys, unsentGroups);
794
795 // now we can create the outermost L3 ECMP group
796 List<GroupBucket> l3ecmpGroupBuckets = new ArrayList<>();
797 for (GroupInfo gi : unsentGroups) {
798 // create ECMP bucket to point to the outer group
799 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700800 ttb.group(new DefaultGroupId(gi.nextGroupDesc.givenGroupId()));
Charles Chan188ebf52015-12-23 00:15:11 -0800801 GroupBucket sbucket = DefaultGroupBucket
802 .createSelectGroupBucket(ttb.build());
803 l3ecmpGroupBuckets.add(sbucket);
804 }
Saurav Das8be4e3a2016-03-11 17:19:07 -0800805 int l3ecmpIndex = getNextAvailableIndex();
806 int l3ecmpGroupId = L3_ECMP_TYPE | (TYPE_MASK & l3ecmpIndex);
807 GroupKey l3ecmpGroupKey = new DefaultGroupKey(
Charles Chan361154b2016-03-24 10:23:39 -0700808 Ofdpa2Pipeline.appKryo.serialize(l3ecmpIndex));
Charles Chan188ebf52015-12-23 00:15:11 -0800809 GroupDescription l3ecmpGroupDesc =
810 new DefaultGroupDescription(
811 deviceId,
812 GroupDescription.Type.SELECT,
813 new GroupBuckets(l3ecmpGroupBuckets),
814 l3ecmpGroupKey,
815 l3ecmpGroupId,
816 nextObj.appId());
817 GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc,
818 l3ecmpGroupBuckets.size(),
819 false);
820
821 // create objects for local and distributed storage
822 allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(l3ecmpGroupKey));
823 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObj);
824
825 // store l3ecmpGroupKey with the ofdpaGroupChain for the nextObjective
826 // that depends on it
827 updatePendingNextObjective(l3ecmpGroupKey, ofdpaGrp);
828
829 log.debug("Trying L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
830 deviceId, Integer.toHexString(l3ecmpGroupId),
831 l3ecmpGroupKey, nextObj.id());
832 // finally we are ready to send the innermost groups
833 for (GroupInfo gi : unsentGroups) {
834 log.debug("Sending innermost group {} in group chain on device {} ",
Charles Chan5b9df8d2016-03-28 22:21:40 -0700835 Integer.toHexString(gi.innerMostGroupDesc.givenGroupId()), deviceId);
836 updatePendingGroups(gi.nextGroupDesc.appCookie(), l3ecmpGce);
837 groupService.addGroup(gi.innerMostGroupDesc);
Charles Chan188ebf52015-12-23 00:15:11 -0800838 }
839
840 }
841
842 /**
843 * Creates group chains for all buckets in a hashed group, and stores the
844 * GroupInfos and GroupKeys for all the groups in the lists passed in, which
845 * should be empty.
846 * <p>
847 * Does not create the top level ECMP group. Does not actually send the
848 * groups to the groupService.
849 *
850 * @param nextObj the Next Objective with buckets that need to be converted
851 * to group chains
852 * @param allGroupKeys a list to store groupKey for each bucket-group-chain
853 * @param unsentGroups a list to store GroupInfo for each bucket-group-chain
854 */
Pier Ventre140a8942016-11-02 07:26:38 -0700855 protected void createHashBucketChains(NextObjective nextObj,
Saurav Das8be4e3a2016-03-11 17:19:07 -0800856 List<Deque<GroupKey>> allGroupKeys,
857 List<GroupInfo> unsentGroups) {
Charles Chan188ebf52015-12-23 00:15:11 -0800858 // break up hashed next objective to multiple groups
859 Collection<TrafficTreatment> buckets = nextObj.next();
860
861 for (TrafficTreatment bucket : buckets) {
862 //figure out how many labels are pushed in each bucket
863 int labelsPushed = 0;
864 MplsLabel innermostLabel = null;
865 for (Instruction ins : bucket.allInstructions()) {
866 if (ins.type() == Instruction.Type.L2MODIFICATION) {
867 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
868 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.MPLS_PUSH) {
869 labelsPushed++;
870 }
871 if (l2ins.subtype() == L2ModificationInstruction.L2SubType.MPLS_LABEL) {
872 if (innermostLabel == null) {
Ray Milkey125572b2016-02-22 16:48:17 -0800873 innermostLabel = ((L2ModificationInstruction.ModMplsLabelInstruction) l2ins).label();
Charles Chan188ebf52015-12-23 00:15:11 -0800874 }
875 }
876 }
877 }
878
879 Deque<GroupKey> gkeyChain = new ArrayDeque<>();
880 // XXX we only deal with 0 and 1 label push right now
881 if (labelsPushed == 0) {
Pier Ventre140a8942016-11-02 07:26:38 -0700882 GroupInfo nolabelGroupInfo;
883 TrafficSelector metaSelector = nextObj.meta();
884 if (metaSelector != null) {
885 if (isNotMplsBos(metaSelector)) {
886 nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
887 nextObj.appId(), true,
888 nextObj.meta());
889 } else {
890 nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
891 nextObj.appId(), false,
892 nextObj.meta());
893 }
894 } else {
895 nolabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
896 nextObj.appId(), false,
897 nextObj.meta());
898 }
Charles Chan188ebf52015-12-23 00:15:11 -0800899 if (nolabelGroupInfo == null) {
900 log.error("Could not process nextObj={} in dev:{}",
901 nextObj.id(), deviceId);
902 return;
903 }
Charles Chan5b9df8d2016-03-28 22:21:40 -0700904 gkeyChain.addFirst(nolabelGroupInfo.innerMostGroupDesc.appCookie());
905 gkeyChain.addFirst(nolabelGroupInfo.nextGroupDesc.appCookie());
Charles Chan188ebf52015-12-23 00:15:11 -0800906
907 // we can't send the inner group description yet, as we have to
908 // create the dependent ECMP group first. So we store..
909 unsentGroups.add(nolabelGroupInfo);
910
911 } else if (labelsPushed == 1) {
912 GroupInfo onelabelGroupInfo = createL2L3Chain(bucket, nextObj.id(),
913 nextObj.appId(), true,
914 nextObj.meta());
915 if (onelabelGroupInfo == null) {
916 log.error("Could not process nextObj={} in dev:{}",
917 nextObj.id(), deviceId);
918 return;
919 }
920 // we need to add another group to this chain - the L3VPN group
921 TrafficTreatment.Builder l3vpnTtb = DefaultTrafficTreatment.builder();
922 l3vpnTtb.pushMpls()
923 .setMpls(innermostLabel)
Charles Chan40132b32017-01-22 00:19:37 -0800924 .group(new DefaultGroupId(onelabelGroupInfo.nextGroupDesc.givenGroupId()));
925 if (supportCopyTtl()) {
926 l3vpnTtb.copyTtlOut();
927 }
928 if (supportSetMplsBos()) {
929 l3vpnTtb.setMplsBos(true);
930 }
931
Charles Chan188ebf52015-12-23 00:15:11 -0800932 GroupBucket l3vpnGrpBkt =
933 DefaultGroupBucket.createIndirectGroupBucket(l3vpnTtb.build());
Saurav Das8be4e3a2016-03-11 17:19:07 -0800934 int l3vpnIndex = getNextAvailableIndex();
935 int l3vpngroupId = MPLS_L3VPN_SUBTYPE | (SUBTYPE_MASK & l3vpnIndex);
936 GroupKey l3vpngroupkey = new DefaultGroupKey(
Charles Chan361154b2016-03-24 10:23:39 -0700937 Ofdpa2Pipeline.appKryo.serialize(l3vpnIndex));
Charles Chan188ebf52015-12-23 00:15:11 -0800938 GroupDescription l3vpnGroupDesc =
939 new DefaultGroupDescription(
940 deviceId,
941 GroupDescription.Type.INDIRECT,
942 new GroupBuckets(Collections.singletonList(
943 l3vpnGrpBkt)),
944 l3vpngroupkey,
945 l3vpngroupId,
946 nextObj.appId());
947 GroupChainElem l3vpnGce = new GroupChainElem(l3vpnGroupDesc, 1, false);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700948 updatePendingGroups(onelabelGroupInfo.nextGroupDesc.appCookie(), l3vpnGce);
Charles Chan188ebf52015-12-23 00:15:11 -0800949
Charles Chan5b9df8d2016-03-28 22:21:40 -0700950 gkeyChain.addFirst(onelabelGroupInfo.innerMostGroupDesc.appCookie());
951 gkeyChain.addFirst(onelabelGroupInfo.nextGroupDesc.appCookie());
Charles Chan188ebf52015-12-23 00:15:11 -0800952 gkeyChain.addFirst(l3vpngroupkey);
953
954 //now we can replace the outerGrpDesc with the one we just created
Charles Chan5b9df8d2016-03-28 22:21:40 -0700955 onelabelGroupInfo.nextGroupDesc = l3vpnGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -0800956
957 // we can't send the innermost group yet, as we have to create
958 // the dependent ECMP group first. So we store ...
959 unsentGroups.add(onelabelGroupInfo);
960
961 log.debug("Trying L3VPN: device:{} gid:{} gkey:{} nextId:{}",
962 deviceId, Integer.toHexString(l3vpngroupId),
963 l3vpngroupkey, nextObj.id());
964
965 } else {
966 log.warn("Driver currently does not handle more than 1 MPLS "
967 + "labels. Not processing nextObjective {}", nextObj.id());
968 return;
969 }
970
971 // all groups in this chain
972 allGroupKeys.add(gkeyChain);
973 }
974 }
975
Saurav Das8be4e3a2016-03-11 17:19:07 -0800976 //////////////////////////////////////
977 // Group Editing
978 //////////////////////////////////////
979
Charles Chan188ebf52015-12-23 00:15:11 -0800980 /**
981 * Adds a bucket to the top level group of a group-chain, and creates the chain.
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700982 * Ensures that bucket being added is not a duplicate, by checking existing
983 * buckets for the same outport.
Charles Chan188ebf52015-12-23 00:15:11 -0800984 *
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700985 * @param nextObjective the bucket information for a next group
Charles Chan188ebf52015-12-23 00:15:11 -0800986 * @param next the representation of the existing group-chain for this next objective
987 */
988 protected void addBucketToGroup(NextObjective nextObjective, NextGroup next) {
Saurav Das1a129a02016-11-18 15:21:57 -0800989 if (nextObjective.type() != NextObjective.Type.HASHED &&
990 nextObjective.type() != NextObjective.Type.BROADCAST) {
Charles Chan188ebf52015-12-23 00:15:11 -0800991 log.warn("AddBuckets not applied to nextType:{} in dev:{} for next:{}",
992 nextObjective.type(), deviceId, nextObjective.id());
Saurav Das1a129a02016-11-18 15:21:57 -0800993 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.UNSUPPORTED);
Charles Chan188ebf52015-12-23 00:15:11 -0800994 return;
995 }
996 if (nextObjective.next().size() > 1) {
Saurav Das1a129a02016-11-18 15:21:57 -0800997 // FIXME - support editing multiple buckets CORD-555
Charles Chan188ebf52015-12-23 00:15:11 -0800998 log.warn("Only one bucket can be added at a time");
Saurav Das1a129a02016-11-18 15:21:57 -0800999 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.UNSUPPORTED);
Charles Chan188ebf52015-12-23 00:15:11 -08001000 return;
1001 }
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001002 // first check to see if bucket being added is not a duplicate of an
1003 // existing bucket. If it is for an existing outport, then its a duplicate.
1004 Set<PortNumber> existingOutPorts = new HashSet<>();
1005 List<Deque<GroupKey>> allActiveKeys = Ofdpa2Pipeline.appKryo.deserialize(next.data());
1006 for (Deque<GroupKey> gkeys : allActiveKeys) {
1007 // get the last group for the outport
1008 Group glast = groupService.getGroup(deviceId, gkeys.peekLast());
1009 if (glast != null && !glast.buckets().buckets().isEmpty()) {
1010 PortNumber op = readOutPortFromTreatment(
1011 glast.buckets().buckets().get(0).treatment());
1012 if (op != null) {
1013 existingOutPorts.add(op);
1014 }
1015 }
1016 }
1017 // only a single bucket being added
1018 TrafficTreatment tt = nextObjective.next().iterator().next();
1019 PortNumber newport = readOutPortFromTreatment(tt);
1020 if (existingOutPorts.contains(newport)) {
Charles Chane5ccd582016-11-10 14:16:54 -08001021 log.info("Attempt to add bucket for existing outport:{} in dev:{} for next:{}",
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001022 newport, deviceId, nextObjective.id());
1023 return;
1024 }
Saurav Das1a129a02016-11-18 15:21:57 -08001025 if (nextObjective.type() == NextObjective.Type.HASHED) {
1026 addBucketToHashGroup(nextObjective, allActiveKeys, newport);
1027 } else if (nextObjective.type() == NextObjective.Type.BROADCAST) {
1028 addBucketToBroadcastGroup(nextObjective, allActiveKeys, newport);
1029 }
1030 }
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001031
Saurav Das1a129a02016-11-18 15:21:57 -08001032 private void addBucketToHashGroup(NextObjective nextObjective,
1033 List<Deque<GroupKey>> allActiveKeys,
1034 PortNumber newport) {
Charles Chan188ebf52015-12-23 00:15:11 -08001035 // storage for all group keys in the chain of groups created
1036 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
1037 List<GroupInfo> unsentGroups = new ArrayList<>();
1038 createHashBucketChains(nextObjective, allGroupKeys, unsentGroups);
1039
Saurav Das1a129a02016-11-18 15:21:57 -08001040 // now we can create the bucket to add to the outermost L3 ECMP group
Charles Chan188ebf52015-12-23 00:15:11 -08001041 GroupInfo gi = unsentGroups.get(0); // only one bucket, so only one group-chain
1042 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
Charles Chan5b9df8d2016-03-28 22:21:40 -07001043 ttb.group(new DefaultGroupId(gi.nextGroupDesc.givenGroupId()));
Charles Chan188ebf52015-12-23 00:15:11 -08001044 GroupBucket sbucket = DefaultGroupBucket.createSelectGroupBucket(ttb.build());
1045
Saurav Das1a129a02016-11-18 15:21:57 -08001046 // retrieve the original L3 ECMP group
1047 Group l3ecmpGroup = retrieveTopLevelGroup(allActiveKeys, nextObjective.id());
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001048 if (l3ecmpGroup == null) {
Saurav Das1a129a02016-11-18 15:21:57 -08001049 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.GROUPMISSING);
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001050 return;
1051 }
Saurav Das1a129a02016-11-18 15:21:57 -08001052 GroupKey l3ecmpGroupKey = l3ecmpGroup.appCookie();
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001053 int l3ecmpGroupId = l3ecmpGroup.id().id();
Charles Chan188ebf52015-12-23 00:15:11 -08001054
1055 // Although GroupDescriptions are not necessary for adding buckets to
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001056 // existing groups, we still use one in the GroupChainElem. When the latter is
Charles Chan188ebf52015-12-23 00:15:11 -08001057 // processed, the info will be extracted for the bucketAdd call to groupService
1058 GroupDescription l3ecmpGroupDesc =
1059 new DefaultGroupDescription(
1060 deviceId,
1061 GroupDescription.Type.SELECT,
1062 new GroupBuckets(Collections.singletonList(sbucket)),
1063 l3ecmpGroupKey,
1064 l3ecmpGroupId,
1065 nextObjective.appId());
1066 GroupChainElem l3ecmpGce = new GroupChainElem(l3ecmpGroupDesc, 1, true);
1067
1068 // update original NextGroup with new bucket-chain
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001069 // If active keys shows only the top-level group without a chain of groups,
1070 // then it represents an empty group. Update by replacing empty chain.
Charles Chan188ebf52015-12-23 00:15:11 -08001071 Deque<GroupKey> newBucketChain = allGroupKeys.get(0);
1072 newBucketChain.addFirst(l3ecmpGroupKey);
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001073 if (allActiveKeys.size() == 1 && allActiveKeys.get(0).size() == 1) {
1074 allActiveKeys.clear();
1075 }
1076 allActiveKeys.add(newBucketChain);
1077 updatePendingNextObjective(l3ecmpGroupKey,
1078 new OfdpaNextGroup(allActiveKeys, nextObjective));
Charles Chan188ebf52015-12-23 00:15:11 -08001079 log.debug("Adding to L3ECMP: device:{} gid:{} gkey:{} nextId:{}",
1080 deviceId, Integer.toHexString(l3ecmpGroupId),
1081 l3ecmpGroupKey, nextObjective.id());
1082 // send the innermost group
1083 log.debug("Sending innermost group {} in group chain on device {} ",
Charles Chan5b9df8d2016-03-28 22:21:40 -07001084 Integer.toHexString(gi.innerMostGroupDesc.givenGroupId()), deviceId);
1085 updatePendingGroups(gi.nextGroupDesc.appCookie(), l3ecmpGce);
1086 groupService.addGroup(gi.innerMostGroupDesc);
Saurav Das1a129a02016-11-18 15:21:57 -08001087 }
Charles Chan188ebf52015-12-23 00:15:11 -08001088
Saurav Das1a129a02016-11-18 15:21:57 -08001089 private void addBucketToBroadcastGroup(NextObjective nextObj,
1090 List<Deque<GroupKey>> allActiveKeys,
1091 PortNumber newport) {
1092 VlanId assignedVlan = Ofdpa2Pipeline.readVlanFromSelector(nextObj.meta());
1093 if (assignedVlan == null) {
1094 log.warn("VLAN ID required by broadcast next obj is missing. "
1095 + "Aborting add bucket to broadcast group for next:{} in dev:{}",
1096 nextObj.id(), deviceId);
1097 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
1098 return;
1099 }
1100
1101 List<GroupInfo> groupInfos = prepareL2InterfaceGroup(nextObj, assignedVlan);
1102
1103 IpPrefix ipDst = Ofdpa2Pipeline.readIpDstFromSelector(nextObj.meta());
1104 if (ipDst != null) {
1105 if (ipDst.isMulticast()) {
1106 addBucketToL3MulticastGroup(nextObj, allActiveKeys,
1107 groupInfos, assignedVlan, newport);
1108 } else {
1109 log.warn("Broadcast NextObj with non-multicast IP address {}", nextObj);
1110 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
1111 return;
1112 }
1113 } else {
1114 addBucketToL2FloodGroup(nextObj, allActiveKeys,
1115 groupInfos, assignedVlan, newport);
1116 }
1117 }
1118
1119 private void addBucketToL2FloodGroup(NextObjective nextObj,
1120 List<Deque<GroupKey>> allActiveKeys,
1121 List<GroupInfo> groupInfos,
1122 VlanId assignedVlan,
1123 PortNumber newport) {
1124 // create the bucket to add to the outermost L2 Flood group
1125 GroupInfo groupInfo = groupInfos.get(0); // only one bucket to add
1126 GroupDescription l2intGrpDesc = groupInfo.nextGroupDesc;
1127 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1128 ttb.group(new DefaultGroupId(l2intGrpDesc.givenGroupId()));
1129 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
1130 // get the group being edited
1131 Group l2floodGroup = retrieveTopLevelGroup(allActiveKeys, nextObj.id());
1132 if (l2floodGroup == null) {
1133 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.GROUPMISSING);
1134 return;
1135 }
1136 GroupKey l2floodGroupKey = l2floodGroup.appCookie();
1137 int l2floodGroupId = l2floodGroup.id().id();
1138
1139 //ensure assignedVlan applies to the chosen group
1140 VlanId expectedVlan = VlanId.vlanId((short) ((l2floodGroupId & 0x0fff0000) >> 16));
1141 if (!expectedVlan.equals(assignedVlan)) {
1142 log.warn("VLAN ID {} does not match Flood group {} to which bucket is "
1143 + "being added, for next:{} in dev:{}. Abort.", assignedVlan,
1144 Integer.toHexString(l2floodGroupId), nextObj.id(), deviceId);
1145 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
1146 }
1147 GroupDescription l2floodGroupDescription =
1148 new DefaultGroupDescription(
1149 deviceId,
1150 GroupDescription.Type.ALL,
1151 new GroupBuckets(Collections.singletonList(abucket)),
1152 l2floodGroupKey,
1153 l2floodGroupId,
1154 nextObj.appId());
1155 GroupChainElem l2floodGce = new GroupChainElem(l2floodGroupDescription, 1, true);
1156
1157 // update original NextGroup with new bucket-chain
1158 // If active keys shows only the top-level group without a chain of groups,
1159 // then it represents an empty group. Update by replacing empty chain.
1160 Deque<GroupKey> newBucketChain = new ArrayDeque<>();
1161 newBucketChain.addFirst(groupInfo.nextGroupDesc.appCookie());
1162 newBucketChain.addFirst(l2floodGroupKey);
1163 if (allActiveKeys.size() == 1 && allActiveKeys.get(0).size() == 1) {
1164 allActiveKeys.clear();
1165 }
1166 allActiveKeys.add(newBucketChain);
1167 updatePendingNextObjective(l2floodGroupKey,
1168 new OfdpaNextGroup(allActiveKeys, nextObj));
1169 log.debug("Adding to L2FLOOD: device:{} gid:{} gkey:{} nextId:{}",
1170 deviceId, Integer.toHexString(l2floodGroupId),
1171 l2floodGroupKey, nextObj.id());
1172 // send the innermost group
1173 log.debug("Sending innermost group {} in group chain on device {} ",
1174 Integer.toHexString(groupInfo.innerMostGroupDesc.givenGroupId()),
1175 deviceId);
1176 updatePendingGroups(groupInfo.nextGroupDesc.appCookie(), l2floodGce);
1177 groupService.addGroup(groupInfo.innerMostGroupDesc);
1178 }
1179
1180 private void addBucketToL3MulticastGroup(NextObjective nextObj,
1181 List<Deque<GroupKey>> allActiveKeys,
1182 List<GroupInfo> groupInfos,
1183 VlanId assignedVlan,
1184 PortNumber newport) {
1185 // create the bucket to add to the outermost L3 Multicast group
1186 GroupInfo groupInfo = groupInfos.get(0); // only one bucket to add
1187 // Points to L3 interface group if there is one.
1188 // Otherwise points to L2 interface group directly.
1189 GroupDescription nextGroupDesc = (groupInfo.nextGroupDesc != null) ?
1190 groupInfo.nextGroupDesc : groupInfo.innerMostGroupDesc;
1191 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
1192 ttb.group(new DefaultGroupId(nextGroupDesc.givenGroupId()));
1193 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
1194
1195 // get the group being edited
1196 Group l3mcastGroup = retrieveTopLevelGroup(allActiveKeys, nextObj.id());
1197 if (l3mcastGroup == null) {
1198 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.GROUPMISSING);
1199 return;
1200 }
1201 GroupKey l3mcastGroupKey = l3mcastGroup.appCookie();
1202 int l3mcastGroupId = l3mcastGroup.id().id();
1203
1204 //ensure assignedVlan applies to the chosen group
1205 VlanId expectedVlan = VlanId.vlanId((short) ((l3mcastGroupId & 0x0fff0000) >> 16));
1206 if (!expectedVlan.equals(assignedVlan)) {
1207 log.warn("VLAN ID {} does not match L3 Mcast group {} to which bucket is "
1208 + "being added, for next:{} in dev:{}. Abort.", assignedVlan,
1209 Integer.toHexString(l3mcastGroupId), nextObj.id(), deviceId);
1210 Ofdpa2Pipeline.fail(nextObj, ObjectiveError.BADPARAMS);
1211 }
1212 GroupDescription l3mcastGroupDescription =
1213 new DefaultGroupDescription(
1214 deviceId,
1215 GroupDescription.Type.ALL,
1216 new GroupBuckets(Collections.singletonList(abucket)),
1217 l3mcastGroupKey,
1218 l3mcastGroupId,
1219 nextObj.appId());
1220 GroupChainElem l3mcastGce = new GroupChainElem(l3mcastGroupDescription,
1221 1, true);
1222
1223 // update original NextGroup with new bucket-chain
1224 Deque<GroupKey> newBucketChain = new ArrayDeque<>();
1225 newBucketChain.addFirst(groupInfo.innerMostGroupDesc.appCookie());
1226 // Add L3 interface group to the chain if there is one.
1227 if (!groupInfo.nextGroupDesc.equals(groupInfo.innerMostGroupDesc)) {
1228 newBucketChain.addFirst(groupInfo.nextGroupDesc.appCookie());
1229 }
1230 newBucketChain.addFirst(l3mcastGroupKey);
1231 // If active keys shows only the top-level group without a chain of groups,
1232 // then it represents an empty group. Update by replacing empty chain.
1233 if (allActiveKeys.size() == 1 && allActiveKeys.get(0).size() == 1) {
1234 allActiveKeys.clear();
1235 }
1236 allActiveKeys.add(newBucketChain);
1237 updatePendingNextObjective(l3mcastGroupKey,
1238 new OfdpaNextGroup(allActiveKeys, nextObj));
1239
1240 updatePendingGroups(groupInfo.nextGroupDesc.appCookie(), l3mcastGce);
1241 // Point next group to inner-most group, if any
1242 if (!groupInfo.nextGroupDesc.equals(groupInfo.innerMostGroupDesc)) {
1243 GroupChainElem innerGce = new GroupChainElem(groupInfo.nextGroupDesc,
1244 1, false);
1245 updatePendingGroups(groupInfo.innerMostGroupDesc.appCookie(), innerGce);
1246 }
1247 log.debug("Adding to L3MCAST: device:{} gid:{} gkey:{} nextId:{}",
1248 deviceId, Integer.toHexString(l3mcastGroupId),
1249 l3mcastGroupKey, nextObj.id());
1250 // send the innermost group
1251 log.debug("Sending innermost group {} in group chain on device {} ",
1252 Integer.toHexString(groupInfo.innerMostGroupDesc.givenGroupId()),
1253 deviceId);
1254 groupService.addGroup(groupInfo.innerMostGroupDesc);
Charles Chan188ebf52015-12-23 00:15:11 -08001255 }
1256
1257 /**
1258 * Removes the bucket in the top level group of a possible group-chain. Does
Saurav Das1a129a02016-11-18 15:21:57 -08001259 * not remove the groups in the group-chain pointed to by this bucket, as they
Charles Chan188ebf52015-12-23 00:15:11 -08001260 * may be in use (referenced by other groups) elsewhere.
1261 *
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001262 * @param nextObjective the bucket information for a next group
Charles Chan188ebf52015-12-23 00:15:11 -08001263 * @param next the representation of the existing group-chain for this next objective
1264 */
1265 protected void removeBucketFromGroup(NextObjective nextObjective, NextGroup next) {
Saurav Das1a129a02016-11-18 15:21:57 -08001266 if (nextObjective.type() != NextObjective.Type.HASHED &&
1267 nextObjective.type() != NextObjective.Type.BROADCAST) {
Charles Chan188ebf52015-12-23 00:15:11 -08001268 log.warn("RemoveBuckets not applied to nextType:{} in dev:{} for next:{}",
1269 nextObjective.type(), deviceId, nextObjective.id());
Saurav Das1a129a02016-11-18 15:21:57 -08001270 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.UNSUPPORTED);
Charles Chan188ebf52015-12-23 00:15:11 -08001271 return;
1272 }
1273 Collection<TrafficTreatment> treatments = nextObjective.next();
1274 TrafficTreatment treatment = treatments.iterator().next();
1275 // find the bucket to remove by noting the outport, and figuring out the
1276 // top-level group in the group-chain that indirectly references the port
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001277 PortNumber portToRemove = readOutPortFromTreatment(treatment);
1278 if (portToRemove == null) {
1279 log.warn("next objective {} has no outport.. cannot remove bucket"
1280 + "from group in dev: {}", nextObjective.id(), deviceId);
Saurav Das1a129a02016-11-18 15:21:57 -08001281 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.BADPARAMS);
Charles Chan188ebf52015-12-23 00:15:11 -08001282 return;
1283 }
1284
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001285 List<Deque<GroupKey>> allActiveKeys = Ofdpa2Pipeline.appKryo.deserialize(next.data());
Charles Chan188ebf52015-12-23 00:15:11 -08001286 Deque<GroupKey> foundChain = null;
1287 int index = 0;
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001288 for (Deque<GroupKey> gkeys : allActiveKeys) {
1289 // last group in group chain should have a single bucket pointing to port
Charles Chan188ebf52015-12-23 00:15:11 -08001290 GroupKey groupWithPort = gkeys.peekLast();
1291 Group group = groupService.getGroup(deviceId, groupWithPort);
1292 if (group == null) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001293 log.warn("Inconsistent group chain found when removing bucket"
1294 + "for next:{} in dev:{}", nextObjective.id(), deviceId);
Charles Chan188ebf52015-12-23 00:15:11 -08001295 continue;
1296 }
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001297 PortNumber pout = readOutPortFromTreatment(
1298 group.buckets().buckets().get(0).treatment());
1299 if (pout.equals(portToRemove)) {
1300 foundChain = gkeys;
Charles Chan188ebf52015-12-23 00:15:11 -08001301 break;
1302 }
1303 index++;
1304 }
Saurav Das1a129a02016-11-18 15:21:57 -08001305 if (foundChain == null) {
Charles Chan188ebf52015-12-23 00:15:11 -08001306 log.warn("Could not find appropriate group-chain for removing bucket"
1307 + " for next id {} in dev:{}", nextObjective.id(), deviceId);
Saurav Das1a129a02016-11-18 15:21:57 -08001308 Ofdpa2Pipeline.fail(nextObjective, ObjectiveError.BADPARAMS);
1309 return;
Charles Chan188ebf52015-12-23 00:15:11 -08001310 }
Saurav Das1a129a02016-11-18 15:21:57 -08001311
1312 //first groupkey is the one we want to modify
1313 GroupKey modGroupKey = foundChain.peekFirst();
1314 Group modGroup = groupService.getGroup(deviceId, modGroupKey);
1315 //second groupkey is the one we wish to remove the reference to
1316 GroupKey pointedGroupKey = null;
1317 int i = 0;
1318 for (GroupKey gk : foundChain) {
1319 if (i++ == 1) {
1320 pointedGroupKey = gk;
1321 break;
1322 }
1323 }
1324 Group pointedGroup = groupService.getGroup(deviceId, pointedGroupKey);
1325 GroupBucket bucket = null;
1326 if (nextObjective.type() == NextObjective.Type.HASHED) {
1327 bucket = DefaultGroupBucket.createSelectGroupBucket(
1328 DefaultTrafficTreatment.builder()
1329 .group(pointedGroup.id())
1330 .build());
1331 } else {
1332 bucket = DefaultGroupBucket.createAllGroupBucket(
1333 DefaultTrafficTreatment.builder()
1334 .group(pointedGroup.id())
1335 .build());
1336 }
1337 GroupBuckets removeBuckets = new GroupBuckets(Collections
1338 .singletonList(bucket));
1339 log.debug("Removing buckets from group id 0x{} pointing to group id 0x{} "
1340 + "for next id {} in device {}", Integer.toHexString(modGroup.id().id()),
1341 Integer.toHexString(pointedGroup.id().id()), nextObjective.id(), deviceId);
1342 groupService.removeBucketsFromGroup(deviceId, modGroupKey,
1343 removeBuckets, modGroupKey,
1344 nextObjective.appId());
1345 // update store
1346 // If the bucket removed was the last bucket in the group, then
1347 // retain an entry for the top level group which still exists.
1348 if (allActiveKeys.size() == 1) {
1349 ArrayDeque<GroupKey> top = new ArrayDeque<>();
1350 top.add(modGroupKey);
1351 allActiveKeys.add(top);
1352 }
1353 allActiveKeys.remove(index);
1354 flowObjectiveStore.putNextGroup(nextObjective.id(),
1355 new OfdpaNextGroup(allActiveKeys,
1356 nextObjective));
Charles Chan188ebf52015-12-23 00:15:11 -08001357 }
1358
1359 /**
1360 * Removes all groups in multiple possible group-chains that represent the next
1361 * objective.
1362 *
1363 * @param nextObjective the next objective to remove
1364 * @param next the NextGroup that represents the existing group-chain for
1365 * this next objective
1366 */
1367 protected void removeGroup(NextObjective nextObjective, NextGroup next) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001368 List<Deque<GroupKey>> allActiveKeys = Ofdpa2Pipeline.appKryo.deserialize(next.data());
Charles Chanfc5c7802016-05-17 13:13:55 -07001369
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001370 List<GroupKey> groupKeys = allActiveKeys.stream()
Charles Chanfc5c7802016-05-17 13:13:55 -07001371 .map(Deque::getFirst).collect(Collectors.toList());
1372 pendingRemoveNextObjectives.put(nextObjective, groupKeys);
1373
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001374 allActiveKeys.forEach(groupChain -> groupChain.forEach(groupKey ->
Charles Chan188ebf52015-12-23 00:15:11 -08001375 groupService.removeGroup(deviceId, groupKey, nextObjective.appId())));
1376 flowObjectiveStore.removeNextGroup(nextObjective.id());
1377 }
1378
Saurav Das8be4e3a2016-03-11 17:19:07 -08001379 //////////////////////////////////////
1380 // Helper Methods and Classes
1381 //////////////////////////////////////
1382
Pier Ventre140a8942016-11-02 07:26:38 -07001383 protected void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001384 List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>();
1385 nextList.add(value);
Charles Chanfc5c7802016-05-17 13:13:55 -07001386 List<OfdpaNextGroup> ret = pendingAddNextObjectives.asMap()
Saurav Das8be4e3a2016-03-11 17:19:07 -08001387 .putIfAbsent(key, nextList);
1388 if (ret != null) {
1389 ret.add(value);
1390 }
1391 }
1392
Charles Chan425854b2016-04-11 15:32:12 -07001393 protected void updatePendingGroups(GroupKey gkey, GroupChainElem gce) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001394 Set<GroupChainElem> gceSet = Collections.newSetFromMap(
1395 new ConcurrentHashMap<GroupChainElem, Boolean>());
1396 gceSet.add(gce);
1397 Set<GroupChainElem> retval = pendingGroups.putIfAbsent(gkey, gceSet);
1398 if (retval != null) {
1399 retval.add(gce);
1400 }
1401 }
1402
Charles Chan188ebf52015-12-23 00:15:11 -08001403 /**
1404 * Processes next element of a group chain. Assumption is that if this
1405 * group points to another group, the latter has already been created
1406 * and this driver has received notification for it. A second assumption is
1407 * that if there is another group waiting for this group then the appropriate
1408 * stores already have the information to act upon the notification for the
1409 * creation of this group.
1410 * <p>
1411 * The processing of the GroupChainElement depends on the number of groups
1412 * this element is waiting on. For all group types other than SIMPLE, a
1413 * GroupChainElement could be waiting on multiple groups.
1414 *
1415 * @param gce the group chain element to be processed next
1416 */
1417 private void processGroupChain(GroupChainElem gce) {
1418 int waitOnGroups = gce.decrementAndGetGroupsWaitedOn();
1419 if (waitOnGroups != 0) {
1420 log.debug("GCE: {} not ready to be processed", gce);
1421 return;
1422 }
1423 log.debug("GCE: {} ready to be processed", gce);
1424 if (gce.addBucketToGroup) {
1425 groupService.addBucketsToGroup(gce.groupDescription.deviceId(),
1426 gce.groupDescription.appCookie(),
1427 gce.groupDescription.buckets(),
1428 gce.groupDescription.appCookie(),
1429 gce.groupDescription.appId());
1430 } else {
1431 groupService.addGroup(gce.groupDescription);
1432 }
1433 }
1434
1435 private class GroupChecker implements Runnable {
1436 @Override
1437 public void run() {
1438 Set<GroupKey> keys = pendingGroups.keySet().stream()
1439 .filter(key -> groupService.getGroup(deviceId, key) != null)
1440 .collect(Collectors.toSet());
Charles Chanfc5c7802016-05-17 13:13:55 -07001441 Set<GroupKey> otherkeys = pendingAddNextObjectives.asMap().keySet().stream()
Charles Chan188ebf52015-12-23 00:15:11 -08001442 .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null)
1443 .collect(Collectors.toSet());
1444 keys.addAll(otherkeys);
1445
Sho SHIMIZUa09e1bb2016-08-01 14:25:25 -07001446 keys.forEach(key ->
Charles Chanfc5c7802016-05-17 13:13:55 -07001447 processPendingAddGroupsOrNextObjs(key, false));
Charles Chan188ebf52015-12-23 00:15:11 -08001448 }
1449 }
1450
Saurav Das8be4e3a2016-03-11 17:19:07 -08001451 private class InnerGroupListener implements GroupListener {
1452 @Override
1453 public void event(GroupEvent event) {
1454 log.trace("received group event of type {}", event.type());
Charles Chanfc5c7802016-05-17 13:13:55 -07001455 switch (event.type()) {
1456 case GROUP_ADDED:
1457 processPendingAddGroupsOrNextObjs(event.subject().appCookie(), true);
1458 break;
1459 case GROUP_REMOVED:
1460 processPendingRemoveNextObjs(event.subject().appCookie());
1461 break;
1462 default:
1463 break;
Saurav Das8be4e3a2016-03-11 17:19:07 -08001464 }
1465 }
1466 }
1467
Charles Chanfc5c7802016-05-17 13:13:55 -07001468 private void processPendingAddGroupsOrNextObjs(GroupKey key, boolean added) {
Charles Chan188ebf52015-12-23 00:15:11 -08001469 //first check for group chain
1470 Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1471 if (gceSet != null) {
1472 for (GroupChainElem gce : gceSet) {
Charles Chan216e3c82016-04-23 14:48:16 -07001473 log.debug("Group service {} group key {} in device {}. "
Saurav Das0fd79d92016-03-07 10:58:36 -08001474 + "Processing next group in group chain with group id 0x{}",
Charles Chan188ebf52015-12-23 00:15:11 -08001475 (added) ? "ADDED" : "processed",
1476 key, deviceId,
1477 Integer.toHexString(gce.groupDescription.givenGroupId()));
1478 processGroupChain(gce);
1479 }
1480 } else {
1481 // otherwise chain complete - check for waiting nextObjectives
Charles Chanfc5c7802016-05-17 13:13:55 -07001482 List<OfdpaNextGroup> nextGrpList = pendingAddNextObjectives.getIfPresent(key);
Charles Chan188ebf52015-12-23 00:15:11 -08001483 if (nextGrpList != null) {
Charles Chanfc5c7802016-05-17 13:13:55 -07001484 pendingAddNextObjectives.invalidate(key);
Charles Chan188ebf52015-12-23 00:15:11 -08001485 nextGrpList.forEach(nextGrp -> {
Charles Chan216e3c82016-04-23 14:48:16 -07001486 log.debug("Group service {} group key {} in device:{}. "
Saurav Das0fd79d92016-03-07 10:58:36 -08001487 + "Done implementing next objective: {} <<-->> gid:0x{}",
Charles Chan188ebf52015-12-23 00:15:11 -08001488 (added) ? "ADDED" : "processed",
1489 key, deviceId, nextGrp.nextObjective().id(),
1490 Integer.toHexString(groupService.getGroup(deviceId, key)
1491 .givenGroupId()));
Charles Chan361154b2016-03-24 10:23:39 -07001492 Ofdpa2Pipeline.pass(nextGrp.nextObjective());
Charles Chan188ebf52015-12-23 00:15:11 -08001493 flowObjectiveStore.putNextGroup(nextGrp.nextObjective().id(), nextGrp);
1494 // check if addBuckets waiting for this completion
1495 NextObjective pendBkt = pendingBuckets
1496 .remove(nextGrp.nextObjective().id());
1497 if (pendBkt != null) {
1498 addBucketToGroup(pendBkt, nextGrp);
1499 }
1500 });
1501 }
1502 }
1503 }
1504
Charles Chanfc5c7802016-05-17 13:13:55 -07001505 private void processPendingRemoveNextObjs(GroupKey key) {
1506 pendingRemoveNextObjectives.asMap().forEach((nextObjective, groupKeys) -> {
1507 if (groupKeys.isEmpty()) {
1508 pendingRemoveNextObjectives.invalidate(nextObjective);
1509 Ofdpa2Pipeline.pass(nextObjective);
1510 } else {
1511 groupKeys.remove(key);
1512 }
1513 });
1514 }
1515
Charles Chan425854b2016-04-11 15:32:12 -07001516 protected int getNextAvailableIndex() {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001517 return (int) nextIndex.incrementAndGet();
1518 }
1519
Charles Chane849c192016-01-11 18:28:54 -08001520 /**
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001521 * Returns the outport in a traffic treatment.
1522 *
1523 * @param tt the treatment
1524 * @return the PortNumber for the outport or null
1525 */
1526 protected static PortNumber readOutPortFromTreatment(TrafficTreatment tt) {
1527 for (Instruction ins : tt.allInstructions()) {
1528 if (ins.type() == Instruction.Type.OUTPUT) {
1529 return ((Instructions.OutputInstruction) ins).port();
1530 }
1531 }
1532 return null;
1533 }
1534
1535 /**
Charles Chane849c192016-01-11 18:28:54 -08001536 * Returns a hash as the L2 Interface Group Key.
1537 *
1538 * Keep the lower 6-bit for port since port number usually smaller than 64.
1539 * Hash other information into remaining 28 bits.
1540 *
1541 * @param deviceId Device ID
1542 * @param vlanId VLAN ID
1543 * @param portNumber Port number
1544 * @return L2 interface group key
1545 */
Pier Ventre140a8942016-11-02 07:26:38 -07001546 protected int l2InterfaceGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
Charles Chane849c192016-01-11 18:28:54 -08001547 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
1548 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
Charles Chand0fd5dc2016-02-16 23:14:49 -08001549 int hash = Objects.hash(deviceId, vlanId, portHigherBits);
Charles Chane849c192016-01-11 18:28:54 -08001550 return L2_INTERFACE_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
1551 }
1552
Saurav Das1a129a02016-11-18 15:21:57 -08001553 private Group retrieveTopLevelGroup(List<Deque<GroupKey>> allActiveKeys,
1554 int nextid) {
1555 GroupKey topLevelGroupKey = null;
1556 if (!allActiveKeys.isEmpty()) {
1557 topLevelGroupKey = allActiveKeys.get(0).peekFirst();
1558 } else {
1559 log.warn("Could not determine top level group while processing"
1560 + "next:{} in dev:{}", nextid, deviceId);
1561 return null;
1562 }
1563 Group topGroup = groupService.getGroup(deviceId, topLevelGroupKey);
1564 if (topGroup == null) {
1565 log.warn("Could not find top level group while processing "
1566 + "next:{} in dev:{}", nextid, deviceId);
1567 }
1568 return topGroup;
1569 }
1570
Charles Chan188ebf52015-12-23 00:15:11 -08001571 /**
1572 * Utility class for moving group information around.
Saurav Das1a129a02016-11-18 15:21:57 -08001573 *
1574 * Example: Suppose we are trying to create a group-chain A-B-C-D, where
1575 * A is the top level group, and D is the inner-most group, typically L2 Interface.
1576 * The innerMostGroupDesc is always D. At various stages of the creation
1577 * process the nextGroupDesc may be C or B. The nextGroupDesc exists to
1578 * inform the referencing group about which group it needs to point to,
1579 * and wait for. In some cases the group chain may simply be A-B. In this case,
1580 * both innerMostGroupDesc and nextGroupDesc will be B.
Charles Chan188ebf52015-12-23 00:15:11 -08001581 */
Charles Chan425854b2016-04-11 15:32:12 -07001582 protected class GroupInfo {
Charles Chan5b9df8d2016-03-28 22:21:40 -07001583 /**
1584 * Description of the inner-most group of the group chain.
1585 * It is always an L2 interface group.
1586 */
1587 private GroupDescription innerMostGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -08001588
Charles Chan5b9df8d2016-03-28 22:21:40 -07001589 /**
1590 * Description of the next group in the group chain.
1591 * It can be L2 interface, L3 interface, L3 unicast, L3 VPN group.
Saurav Das1a129a02016-11-18 15:21:57 -08001592 * It is possible that nextGroupDesc is the same as the innerMostGroup.
Charles Chan5b9df8d2016-03-28 22:21:40 -07001593 */
1594 private GroupDescription nextGroupDesc;
1595
1596 GroupInfo(GroupDescription innerMostGroupDesc, GroupDescription nextGroupDesc) {
1597 this.innerMostGroupDesc = innerMostGroupDesc;
1598 this.nextGroupDesc = nextGroupDesc;
Charles Chan188ebf52015-12-23 00:15:11 -08001599 }
Pier Ventre140a8942016-11-02 07:26:38 -07001600
1601 /**
1602 * Getter for innerMostGroupDesc.
1603 *
1604 * @return the inner most group description
1605 */
1606 public GroupDescription getInnerMostGroupDesc() {
1607 return innerMostGroupDesc;
1608 }
1609
1610 /**
1611 * Getter for the next group description.
1612 *
1613 * @return the next group description
1614 */
1615 public GroupDescription getNextGroupDesc() {
1616 return nextGroupDesc;
1617 }
Charles Chan188ebf52015-12-23 00:15:11 -08001618 }
1619
1620 /**
1621 * Represents an entire group-chain that implements a Next-Objective from
1622 * the application. The objective is represented as a list of deques, where
1623 * each deque is a separate chain of groups.
1624 * <p>
1625 * For example, an ECMP group with 3 buckets, where each bucket points to
1626 * a group chain of L3 Unicast and L2 interface groups will look like this:
1627 * <ul>
1628 * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1629 * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1630 * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
1631 * </ul>
1632 * where the first element of each deque is the same, representing the
1633 * top level ECMP group, while every other element represents a unique groupKey.
1634 * <p>
1635 * Also includes information about the next objective that
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001636 * resulted in these group-chains.
Charles Chan188ebf52015-12-23 00:15:11 -08001637 *
1638 */
1639 protected class OfdpaNextGroup implements NextGroup {
1640 private final NextObjective nextObj;
1641 private final List<Deque<GroupKey>> gkeys;
1642
1643 public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
Charles Chan188ebf52015-12-23 00:15:11 -08001644 this.nextObj = nextObj;
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001645 this.gkeys = gkeys;
Charles Chan188ebf52015-12-23 00:15:11 -08001646 }
1647
1648 public NextObjective nextObjective() {
1649 return nextObj;
1650 }
1651
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001652 public List<Deque<GroupKey>> groupKeys() {
1653 return gkeys;
1654 }
1655
Charles Chan188ebf52015-12-23 00:15:11 -08001656 @Override
1657 public byte[] data() {
Charles Chan361154b2016-03-24 10:23:39 -07001658 return Ofdpa2Pipeline.appKryo.serialize(gkeys);
Charles Chan188ebf52015-12-23 00:15:11 -08001659 }
1660 }
1661
1662 /**
1663 * Represents a group element that is part of a chain of groups.
1664 * Stores enough information to create a Group Description to add the group
1665 * to the switch by requesting the Group Service. Objects instantiating this
1666 * class are meant to be temporary and live as long as it is needed to wait for
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001667 * referenced groups in the group chain to be created.
Charles Chan188ebf52015-12-23 00:15:11 -08001668 */
Charles Chan425854b2016-04-11 15:32:12 -07001669 protected class GroupChainElem {
Charles Chan188ebf52015-12-23 00:15:11 -08001670 private GroupDescription groupDescription;
1671 private AtomicInteger waitOnGroups;
1672 private boolean addBucketToGroup;
1673
1674 GroupChainElem(GroupDescription groupDescription, int waitOnGroups,
1675 boolean addBucketToGroup) {
1676 this.groupDescription = groupDescription;
1677 this.waitOnGroups = new AtomicInteger(waitOnGroups);
1678 this.addBucketToGroup = addBucketToGroup;
1679 }
1680
1681 /**
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001682 * This method atomically decrements the counter for the number of
Charles Chan188ebf52015-12-23 00:15:11 -08001683 * groups this GroupChainElement is waiting on, for notifications from
1684 * the Group Service. When this method returns a value of 0, this
1685 * GroupChainElement is ready to be processed.
1686 *
1687 * @return integer indication of the number of notifications being waited on
1688 */
1689 int decrementAndGetGroupsWaitedOn() {
1690 return waitOnGroups.decrementAndGet();
1691 }
1692
1693 @Override
1694 public String toString() {
1695 return (Integer.toHexString(groupDescription.givenGroupId()) +
1696 " groupKey: " + groupDescription.appCookie() +
1697 " waiting-on-groups: " + waitOnGroups.get() +
1698 " addBucketToGroup: " + addBucketToGroup +
1699 " device: " + deviceId);
1700 }
1701 }
Pier Ventre140a8942016-11-02 07:26:38 -07001702
1703 /**
1704 * Helper enum to handle the different MPLS group
1705 * types.
1706 */
1707 protected enum OfdpaMplsGroupSubType {
1708
1709 MPLS_INTF((short) 0),
1710
1711 L2_VPN((short) 1),
1712
1713 L3_VPN((short) 2),
1714
1715 MPLS_TUNNEL_LABEL_1((short) 3),
1716
1717 MPLS_TUNNEL_LABEL_2((short) 4),
1718
1719 MPLS_SWAP_LABEL((short) 5),
1720
1721 MPLS_ECMP((short) 8);
1722
1723 private short value;
1724
1725 public static final int OFDPA_GROUP_TYPE_SHIFT = 28;
1726 public static final int OFDPA_MPLS_SUBTYPE_SHIFT = 24;
1727
1728 OfdpaMplsGroupSubType(short value) {
1729 this.value = value;
1730 }
1731
1732 /**
1733 * Gets the value as an short.
1734 *
1735 * @return the value as an short
1736 */
1737 public short getValue() {
1738 return this.value;
1739 }
1740
1741 }
1742
1743 /**
1744 * Creates MPLS Label group id given a sub type and
1745 * the index.
1746 *
1747 * @param subType the MPLS Label group sub type
1748 * @param index the index of the group
1749 * @return the OFDPA group id
1750 */
1751 public Integer makeMplsLabelGroupId(OfdpaMplsGroupSubType subType, int index) {
1752 index = index & 0x00FFFFFF;
1753 return index | (9 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
1754 }
1755
1756 /**
1757 * Creates MPLS Forwarding group id given a sub type and
1758 * the index.
1759 *
1760 * @param subType the MPLS forwarding group sub type
1761 * @param index the index of the group
1762 * @return the OFDPA group id
1763 */
1764 public Integer makeMplsForwardingGroupId(OfdpaMplsGroupSubType subType, int index) {
1765 index = index & 0x00FFFFFF;
1766 return index | (10 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
1767 }
Charles Chan188ebf52015-12-23 00:15:11 -08001768}