blob: 31b92a88a03b3bf29cf0b991bf5d6f460dcc5872 [file] [log] [blame]
Yi Tsengef19de12017-04-24 11:33:05 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Yi Tsengef19de12017-04-24 11:33:05 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.driver.pipeline.ofdpa;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.Lists;
21import com.google.common.collect.Sets;
22import org.onlab.packet.VlanId;
23import org.onosproject.core.GroupId;
24import org.onosproject.net.DeviceId;
25import org.onosproject.net.PortNumber;
26import org.onosproject.net.behaviour.NextGroup;
27import org.onosproject.net.flow.DefaultTrafficTreatment;
28import org.onosproject.net.flow.TrafficSelector;
29import org.onosproject.net.flow.TrafficTreatment;
Andrea Campanella7c977b92018-05-30 21:39:45 -070030import org.onosproject.net.flow.criteria.Criterion;
31import org.onosproject.net.flow.criteria.VlanIdCriterion;
Yi Tsengef19de12017-04-24 11:33:05 -070032import org.onosproject.net.flow.instructions.Instruction;
33import org.onosproject.net.flow.instructions.Instructions;
Saurav Das7bcbe702017-06-13 15:35:54 -070034import org.onosproject.net.flow.instructions.L2ModificationInstruction;
35import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
Yi Tsengef19de12017-04-24 11:33:05 -070036import org.onosproject.net.flowobjective.NextObjective;
37import org.onosproject.net.group.DefaultGroupBucket;
38import org.onosproject.net.group.DefaultGroupKey;
39import org.onosproject.net.group.Group;
40import org.onosproject.net.group.GroupBucket;
41import org.onosproject.net.group.GroupDescription;
42import org.onosproject.net.group.GroupKey;
43import org.onosproject.net.group.GroupService;
44import org.slf4j.Logger;
45
Saurav Dasceccf242017-08-03 18:30:35 -070046import java.util.ArrayList;
Yi Tsengef19de12017-04-24 11:33:05 -070047import java.util.Deque;
48import java.util.List;
49import java.util.Objects;
50import java.util.Set;
51import java.util.concurrent.atomic.AtomicInteger;
52import java.util.stream.Collectors;
53
54import static org.onosproject.driver.pipeline.ofdpa.Ofdpa2Pipeline.isNotMplsBos;
55import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.OfdpaMplsGroupSubType.OFDPA_GROUP_TYPE_SHIFT;
56import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.OfdpaMplsGroupSubType.OFDPA_MPLS_SUBTYPE_SHIFT;
57import static org.onosproject.net.flowobjective.NextObjective.Type.HASHED;
58import static org.slf4j.LoggerFactory.getLogger;
59
60public final class OfdpaGroupHandlerUtility {
61 /*
62 * OFDPA requires group-id's to have a certain form.
63 * L2 Interface Groups have <4bits-0><12bits-vlanId><16bits-portId>
64 * L3 Unicast Groups have <4bits-2><28bits-index>
Andrea Campanella7c977b92018-05-30 21:39:45 -070065 * L3 Unicast Groups for double-vlan have <4bits-2><1bit-1><12bits-vlanId><15bits-portId>
Yi Tsengef19de12017-04-24 11:33:05 -070066 * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index>
67 * L3 ECMP Groups have <4bits-7><28bits-index>
68 * L2 Flood Groups have <4bits-4><12bits-vlanId><16bits-index>
69 * L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
70 */
Ray Milkey9c9cde42018-01-12 14:22:06 -080071 static final int L2_INTERFACE_TYPE = 0x00000000;
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070072 static final int L2_UNFILTERED_TYPE = 0xb0000000;
Ray Milkey9c9cde42018-01-12 14:22:06 -080073 static final int L3_INTERFACE_TYPE = 0x50000000;
74 static final int L3_UNICAST_TYPE = 0x20000000;
75 static final int L3_MULTICAST_TYPE = 0x60000000;
76 static final int MPLS_INTERFACE_TYPE = 0x90000000;
77 static final int MPLS_L3VPN_SUBTYPE = 0x92000000;
78 static final int L3_ECMP_TYPE = 0x70000000;
79 static final int L2_FLOOD_TYPE = 0x40000000;
Yi Tsengef19de12017-04-24 11:33:05 -070080
Ray Milkey9c9cde42018-01-12 14:22:06 -080081 static final int TYPE_MASK = 0x0fffffff;
82 static final int SUBTYPE_MASK = 0x00ffffff;
83 static final int TYPE_VLAN_MASK = 0x0000ffff;
Andrea Campanella7c977b92018-05-30 21:39:45 -070084 static final int TYPE_L3UG_DOUBLE_VLAN_MASK = 0x08000000;
Yi Tsengef19de12017-04-24 11:33:05 -070085
Ray Milkey9c9cde42018-01-12 14:22:06 -080086 static final int THREE_BIT_MASK = 0x0fff;
87 static final int FOUR_BIT_MASK = 0xffff;
88 static final int PORT_LEN = 16;
Yi Tsengef19de12017-04-24 11:33:05 -070089
Ray Milkey9c9cde42018-01-12 14:22:06 -080090 static final int PORT_LOWER_BITS_MASK = 0x3f;
91 static final long PORT_HIGHER_BITS_MASK = ~PORT_LOWER_BITS_MASK;
Yi Tsengef19de12017-04-24 11:33:05 -070092
Ray Milkey9c9cde42018-01-12 14:22:06 -080093 static final String HEX_PREFIX = "0x";
94 private static final Logger log = getLogger(OfdpaGroupHandlerUtility.class);
Yi Tsengef19de12017-04-24 11:33:05 -070095
96 private OfdpaGroupHandlerUtility() {
97 // Utility classes should not have a public or default constructor.
98 }
99
100 /**
101 * Returns the outport in a traffic treatment.
102 *
103 * @param tt the treatment
104 * @return the PortNumber for the outport or null
105 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800106 static PortNumber readOutPortFromTreatment(TrafficTreatment tt) {
Yi Tsengef19de12017-04-24 11:33:05 -0700107 for (Instruction ins : tt.allInstructions()) {
108 if (ins.type() == Instruction.Type.OUTPUT) {
109 return ((Instructions.OutputInstruction) ins).port();
110 }
111 }
112 return null;
113 }
114
115 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700116 * Returns the MPLS label-id in a traffic treatment.
117 *
118 * @param tt the traffic treatment
119 * @return an integer representing the MPLS label-id, or -1 if not found
120 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800121 static int readLabelFromTreatment(TrafficTreatment tt) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700122 for (Instruction ins : tt.allInstructions()) {
123 if (ins.type() == Instruction.Type.L2MODIFICATION) {
124 L2ModificationInstruction insl2 = (L2ModificationInstruction) ins;
125 if (insl2.subtype() == L2SubType.MPLS_LABEL) {
126 return ((L2ModificationInstruction.ModMplsLabelInstruction) insl2)
127 .label().id();
128 }
129 }
130 }
131 return -1;
132 }
133
134 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700135 * Helper enum to handle the different MPLS group
136 * types.
137 */
138 public enum OfdpaMplsGroupSubType {
139 MPLS_INTF((short) 0),
140 L2_VPN((short) 1),
141 L3_VPN((short) 2),
142 MPLS_TUNNEL_LABEL_1((short) 3),
143 MPLS_TUNNEL_LABEL_2((short) 4),
144 MPLS_SWAP_LABEL((short) 5),
145 MPLS_ECMP((short) 8);
146
147 private short value;
148 public static final int OFDPA_GROUP_TYPE_SHIFT = 28;
149 public static final int OFDPA_MPLS_SUBTYPE_SHIFT = 24;
150
151 OfdpaMplsGroupSubType(short value) {
152 this.value = value;
153 }
154
155 /**
156 * Gets the value as an short.
157 *
158 * @return the value as an short
159 */
160 public short getValue() {
161 return this.value;
162 }
163
164 }
165
166 /**
167 * Creates MPLS Label group id given a sub type and
168 * the index.
169 *
170 * @param subType the MPLS Label group sub type
171 * @param index the index of the group
172 * @return the OFDPA group id
173 */
174 public static Integer makeMplsLabelGroupId(OfdpaMplsGroupSubType subType, int index) {
175 index = index & 0x00FFFFFF;
176 return index | (9 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
177 }
178
179 /**
180 * Creates MPLS Forwarding group id given a sub type and
181 * the index.
182 *
183 * @param subType the MPLS forwarding group sub type
184 * @param index the index of the group
185 * @return the OFDPA group id
186 */
187 public static Integer makeMplsForwardingGroupId(OfdpaMplsGroupSubType subType, int index) {
188 index = index & 0x00FFFFFF;
189 return index | (10 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
190 }
191
192 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700193 * Returns the set of existing output ports in the group represented by
194 * allActiveKeys.
Yi Tsengef19de12017-04-24 11:33:05 -0700195 *
196 * @param allActiveKeys list of group key chain
197 * @param groupService the group service to get group information
198 * @param deviceId the device id to get group
199 * @return a set of output port from the list of group key chain
200 */
201 public static Set<PortNumber> getExistingOutputPorts(List<Deque<GroupKey>> allActiveKeys,
202 GroupService groupService,
203 DeviceId deviceId) {
204 Set<PortNumber> existingPorts = Sets.newHashSet();
205
206 allActiveKeys.forEach(keyChain -> {
207 GroupKey ifaceGroupKey = keyChain.peekLast();
208 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
209 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
210 ifaceGroup.buckets().buckets().forEach(bucket -> {
211 PortNumber portNumber = readOutPortFromTreatment(bucket.treatment());
212 if (portNumber != null) {
213 existingPorts.add(portNumber);
214 }
215 });
216 }
217 });
218 return existingPorts;
219 }
220
221 /**
Saurav Dasceccf242017-08-03 18:30:35 -0700222 * Returns a list of all indices in the allActiveKeys list (that represents
223 * a group) if the list element (a bucket or group-chain) has treatments
224 * that match the given outport and label.
Saurav Das7bcbe702017-06-13 15:35:54 -0700225 *
226 * @param allActiveKeys the representation of the group
227 * @param groupService groups service for querying group information
228 * @param deviceId the device id for the device that contains the group
229 * @param portToMatch the port to match in the group buckets
230 * @param labelToMatch the MPLS label-id to match in the group buckets
Saurav Dasceccf242017-08-03 18:30:35 -0700231 * @return a list of indexes in the allActiveKeys list where the list element
232 * has treatments that match the given portToMatch and labelToMatch.
233 * Could be empty if no list elements were found to match the given
234 * port and label.
Saurav Das7bcbe702017-06-13 15:35:54 -0700235 */
Saurav Dasceccf242017-08-03 18:30:35 -0700236 public static List<Integer> existingPortAndLabel(
237 List<Deque<GroupKey>> allActiveKeys,
Saurav Das7bcbe702017-06-13 15:35:54 -0700238 GroupService groupService,
239 DeviceId deviceId,
240 PortNumber portToMatch,
241 int labelToMatch) {
Saurav Dasceccf242017-08-03 18:30:35 -0700242 List<Integer> indices = new ArrayList<>();
243 int index = 0;
Saurav Das7bcbe702017-06-13 15:35:54 -0700244 for (Deque<GroupKey> keyChain : allActiveKeys) {
245 GroupKey ifaceGroupKey = keyChain.peekLast();
246 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
247 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
248 PortNumber portNumber = readOutPortFromTreatment(
249 ifaceGroup.buckets().buckets().iterator().next().treatment());
250 if (portNumber != null && portNumber.equals(portToMatch)) {
251 // check for label in the 2nd group of this chain
252 GroupKey secondKey = (GroupKey) keyChain.toArray()[1];
253 Group secondGroup = groupService.getGroup(deviceId, secondKey);
254 if (secondGroup != null &&
255 !secondGroup.buckets().buckets().isEmpty()) {
256 int label = readLabelFromTreatment(
257 secondGroup.buckets().buckets()
258 .iterator().next().treatment());
259 if (label == labelToMatch) {
Saurav Dasceccf242017-08-03 18:30:35 -0700260 indices.add(index);
Saurav Das7bcbe702017-06-13 15:35:54 -0700261 }
262 }
263 }
264 }
Saurav Dasceccf242017-08-03 18:30:35 -0700265 index++;
Saurav Das7bcbe702017-06-13 15:35:54 -0700266 }
267
Saurav Dasceccf242017-08-03 18:30:35 -0700268 return indices;
Saurav Das7bcbe702017-06-13 15:35:54 -0700269 }
270
Pier Luigiec6ac422018-01-29 10:30:59 +0100271 /**
272 * Get indices to remove comparing next group with next objective.
273 *
274 * @param allActiveKeys the representation of the group
275 * @param nextObjective the next objective to verify
276 * @param groupService groups service for querying group information
277 * @param deviceId the device id for the device that contains the group
278 * @return a list of indexes in the allActiveKeys to remove.
279 */
280 public static List<Integer> indicesToRemoveFromNextGroup(List<Deque<GroupKey>> allActiveKeys,
281 NextObjective nextObjective,
282 GroupService groupService,
283 DeviceId deviceId) {
284 List<Integer> indicesToRemove = Lists.newArrayList();
285 int index = 0;
286 // Iterate over the chain in the next data
287 for (Deque<GroupKey> keyChain : allActiveKeys) {
288 // Valid chain should have at least two elements
289 if (keyChain.size() >= 2) {
290 // Get last group (l2if) and retrieve port number
291 GroupKey ifaceGroupKey = keyChain.peekLast();
292 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
293 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
294 PortNumber portNumber = readOutPortFromTreatment(
295 ifaceGroup.buckets().buckets().iterator().next().treatment());
296 // If there is not a port number continue
297 if (portNumber != null) {
298 // check for label in the 2nd group of this chain
299 GroupKey secondKey = (GroupKey) keyChain.toArray()[1];
300 Group secondGroup = groupService.getGroup(deviceId, secondKey);
301 // If there is not a second group or there are no buckets continue
302 if (secondGroup != null && !secondGroup.buckets().buckets().isEmpty()) {
303 // Get label or -1
304 int label = readLabelFromTreatment(
305 secondGroup.buckets().buckets()
306 .iterator().next().treatment());
307 // Iterate over the next treatments looking for the port and the label
308 boolean matches = false;
309 for (TrafficTreatment t : nextObjective.next()) {
310 PortNumber tPort = readOutPortFromTreatment(t);
311 int tLabel = readLabelFromTreatment(t);
312 if (tPort != null && tPort.equals(portNumber) && tLabel == label) {
313 // We found it, exit
314 matches = true;
315 break;
316 }
317 }
318 // Not found, we have to remove it
319 if (!matches) {
320 indicesToRemove.add(index);
321 }
322 }
323 }
324 }
325 }
326 index++;
327 }
328 return indicesToRemove;
329 }
330
Saurav Das7bcbe702017-06-13 15:35:54 -0700331 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700332 * The purpose of this function is to verify if the hashed next
333 * objective is supported by the current pipeline.
334 *
335 * @param nextObjective the hashed objective to verify
336 * @return true if the hashed objective is supported. Otherwise false.
337 */
338 public static boolean verifyHashedNextObjective(NextObjective nextObjective) {
339 // if it is not hashed, there is something wrong;
340 if (nextObjective.type() != HASHED) {
341 return false;
342 }
343 // The case non supported is the MPLS-ECMP. For now, we try
344 // to create a MPLS-ECMP for the transport of a VPWS. The
345 // necessary info are contained in the meta selector. In particular
346 // we are looking for the case of BoS==False;
347 TrafficSelector metaSelector = nextObjective.meta();
348 if (metaSelector != null && isNotMplsBos(metaSelector)) {
349 return false;
350 }
351
352 return true;
353 }
354
355 /**
356 * Generates a list of group buckets from given list of group information
357 * and group bucket type.
358 *
359 * @param groupInfos a list of group information
360 * @param bucketType group bucket type
361 * @return list of group bucket generate from group information
362 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800363 static List<GroupBucket> generateNextGroupBuckets(List<GroupInfo> groupInfos,
Yi Tsengef19de12017-04-24 11:33:05 -0700364 GroupDescription.Type bucketType) {
365 List<GroupBucket> newBuckets = Lists.newArrayList();
366
367 groupInfos.forEach(groupInfo -> {
368 GroupDescription groupDesc = groupInfo.nextGroupDesc();
369 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
370 treatmentBuilder.group(new GroupId(groupDesc.givenGroupId()));
371 GroupBucket newBucket = null;
372 switch (bucketType) {
373 case ALL:
374 newBucket =
375 DefaultGroupBucket.createAllGroupBucket(treatmentBuilder.build());
376 break;
377 case INDIRECT:
378 newBucket =
379 DefaultGroupBucket.createIndirectGroupBucket(treatmentBuilder.build());
380 break;
381 case SELECT:
382 newBucket =
383 DefaultGroupBucket.createSelectGroupBucket(treatmentBuilder.build());
384 break;
385 case FAILOVER:
386 // TODO: support failover bucket type
387 default:
388 log.warn("Unknown bucket type: {}", bucketType);
389 break;
390 }
391
392 if (newBucket != null) {
393 newBuckets.add(newBucket);
394 }
395
396 });
397
398 return ImmutableList.copyOf(newBuckets);
399 }
400
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +0900401 static List<GroupBucket> createL3MulticastBucket(List<GroupInfo> groupInfos) {
402 List<GroupBucket> l3McastBuckets = new ArrayList<>();
403 // For each inner group
404 groupInfos.forEach(groupInfo -> {
405 // Points to L3 interface group if there is one.
406 // Otherwise points to L2 interface group directly.
407 GroupDescription nextGroupDesc = (groupInfo.nextGroupDesc() != null) ?
408 groupInfo.nextGroupDesc() : groupInfo.innerMostGroupDesc();
409 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
410 ttb.group(new GroupId(nextGroupDesc.givenGroupId()));
411 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
412 l3McastBuckets.add(abucket);
413 });
414 // Done return the new list of buckets
415 return l3McastBuckets;
416 }
417
418 static Group retrieveTopLevelGroup(List<Deque<GroupKey>> allActiveKeys,
419 DeviceId deviceId,
420 GroupService groupService,
421 int nextid) {
422 GroupKey topLevelGroupKey;
423 if (!allActiveKeys.isEmpty()) {
424 topLevelGroupKey = allActiveKeys.get(0).peekFirst();
425 } else {
426 log.warn("Could not determine top level group while processing"
427 + "next:{} in dev:{}", nextid, deviceId);
428 return null;
429 }
430 Group topGroup = groupService.getGroup(deviceId, topLevelGroupKey);
431 if (topGroup == null) {
432 log.warn("Could not find top level group while processing "
433 + "next:{} in dev:{}", nextid, deviceId);
434 }
435 return topGroup;
436 }
437
Yi Tsengef19de12017-04-24 11:33:05 -0700438 /**
439 * Extracts VlanId from given group ID.
440 *
441 * @param groupId the group ID
442 * @return vlan id of the group
443 */
444 public static VlanId extractVlanIdFromGroupId(int groupId) {
445 // Extract the 9th to 20th bit from group id as vlan id.
446 short vlanId = (short) ((groupId & 0x0fff0000) >> 16);
447 return VlanId.vlanId(vlanId);
448 }
449
450 public static GroupKey l2FloodGroupKey(VlanId vlanId, DeviceId deviceId) {
451 int hash = Objects.hash(deviceId, vlanId);
452 hash = L2_FLOOD_TYPE | TYPE_MASK & hash;
453 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
454 }
455
456 public static int l2GroupId(VlanId vlanId, long portNum) {
457 return L2_INTERFACE_TYPE | (vlanId.toShort() << 16) | (int) portNum;
458 }
459
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700460 public static int l2UnfilteredGroupId(long portNum) {
461 return L2_UNFILTERED_TYPE | (int) portNum;
462 }
463
Yi Tsengef19de12017-04-24 11:33:05 -0700464 /**
465 * Returns a hash as the L2 Interface Group Key.
466 *
467 * Keep the lower 6-bit for port since port number usually smaller than 64.
468 * Hash other information into remaining 28 bits.
469 *
470 * @param deviceId Device ID
471 * @param vlanId VLAN ID
472 * @param portNumber Port number
473 * @return L2 interface group key
474 */
475 public static int l2InterfaceGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
476 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
477 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
478 int hash = Objects.hash(deviceId, vlanId, portHigherBits);
479 return L2_INTERFACE_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
480 }
481
482 /**
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700483 * Returns a hash as the L2 Unfiltered Interface Group Key.
484 *
485 * Keep the lower 6-bit for port since port number usually smaller than 64.
486 * Hash other information into remaining 28 bits.
487 *
488 * @param deviceId Device ID
489 * @param portNumber Port number
490 * @return L2 unfiltered interface group key
491 */
492 public static int l2UnfilteredGroupKey(DeviceId deviceId, long portNumber) {
493 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
494 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
495 int hash = Objects.hash(deviceId, portHigherBits);
496 return L2_UNFILTERED_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
497 }
498
499 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700500 * Utility class for moving group information around.
501 *
502 * Example: Suppose we are trying to create a group-chain A-B-C-D, where
503 * A is the top level group, and D is the inner-most group, typically L2 Interface.
504 * The innerMostGroupDesc is always D. At various stages of the creation
505 * process the nextGroupDesc may be C or B. The nextGroupDesc exists to
506 * inform the referencing group about which group it needs to point to,
507 * and wait for. In some cases the group chain may simply be A-B. In this case,
508 * both innerMostGroupDesc and nextGroupDesc will be B.
509 */
510 public static class GroupInfo {
511 /**
512 * Description of the inner-most group of the group chain.
513 * It is always an L2 interface group.
514 */
515 private GroupDescription innerMostGroupDesc;
516
517 /**
518 * Description of the next group in the group chain.
519 * It can be L2 interface, L3 interface, L3 unicast, L3 VPN group.
520 * It is possible that nextGroupDesc is the same as the innerMostGroup.
521 */
522 private GroupDescription nextGroupDesc;
523
524 GroupInfo(GroupDescription innerMostGroupDesc, GroupDescription nextGroupDesc) {
525 this.innerMostGroupDesc = innerMostGroupDesc;
526 this.nextGroupDesc = nextGroupDesc;
527 }
528
529 /**
530 * Getter for innerMostGroupDesc.
531 *
532 * @return the inner most group description
533 */
534 public GroupDescription innerMostGroupDesc() {
535 return innerMostGroupDesc;
536 }
537
538 /**
539 * Getter for the next group description.
540 *
541 * @return the next group description
542 */
543 public GroupDescription nextGroupDesc() {
544 return nextGroupDesc;
545 }
546
547 /**
548 * Setter of nextGroupDesc.
549 *
550 * @param nextGroupDesc the given value to set
551 */
552 public void nextGroupDesc(GroupDescription nextGroupDesc) {
553 this.nextGroupDesc = nextGroupDesc;
554 }
555 }
556
557 /**
558 * Represents an entire group-chain that implements a Next-Objective from
559 * the application. The objective is represented as a list of deques, where
560 * each deque is a separate chain of groups.
561 * <p>
562 * For example, an ECMP group with 3 buckets, where each bucket points to
563 * a group chain of L3 Unicast and L2 interface groups will look like this:
564 * <ul>
565 * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
566 * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
567 * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
568 * </ul>
569 * where the first element of each deque is the same, representing the
570 * top level ECMP group, while every other element represents a unique groupKey.
571 * <p>
572 * Also includes information about the next objective that
573 * resulted in these group-chains.
574 *
575 */
576 public static class OfdpaNextGroup implements NextGroup {
577 private final NextObjective nextObj;
578 private final List<Deque<GroupKey>> gkeys;
579
580 public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
581 this.nextObj = nextObj;
582 this.gkeys = gkeys;
583 }
584
585 public NextObjective nextObjective() {
586 return nextObj;
587 }
588
Saurav Dasc88d4662017-05-15 15:34:25 -0700589 public List<Deque<GroupKey>> allKeys() {
590 return gkeys;
591 }
592
Yi Tsengef19de12017-04-24 11:33:05 -0700593 @Override
594 public byte[] data() {
595 return Ofdpa2Pipeline.appKryo.serialize(gkeys);
596 }
597 }
598
599 /**
600 * Represents a group element that is part of a chain of groups.
601 * Stores enough information to create a Group Description to add the group
602 * to the switch by requesting the Group Service. Objects instantiating this
603 * class are meant to be temporary and live as long as it is needed to wait for
604 * referenced groups in the group chain to be created.
605 */
606 public static class GroupChainElem {
607 private GroupDescription groupDescription;
608 private AtomicInteger waitOnGroups;
609 private boolean addBucketToGroup;
610 private DeviceId deviceId;
611
612 public GroupChainElem(GroupDescription groupDescription, int waitOnGroups,
613 boolean addBucketToGroup, DeviceId deviceId) {
614 this.groupDescription = groupDescription;
615 this.waitOnGroups = new AtomicInteger(waitOnGroups);
616 this.addBucketToGroup = addBucketToGroup;
617 this.deviceId = deviceId;
618 }
619
620 /**
621 * This method atomically decrements the counter for the number of
622 * groups this GroupChainElement is waiting on, for notifications from
623 * the Group Service. When this method returns a value of 0, this
624 * GroupChainElement is ready to be processed.
625 *
626 * @return integer indication of the number of notifications being waited on
627 */
628 int decrementAndGetGroupsWaitedOn() {
629 return waitOnGroups.decrementAndGet();
630 }
631
632 public GroupDescription groupDescription() {
633 return groupDescription;
634 }
635
636 public boolean addBucketToGroup() {
637 return addBucketToGroup;
638 }
639
640 @Override
641 public String toString() {
642 return (Integer.toHexString(groupDescription.givenGroupId()) +
643 " groupKey: " + groupDescription.appCookie() +
644 " waiting-on-groups: " + waitOnGroups.get() +
645 " addBucketToGroup: " + addBucketToGroup +
646 " device: " + deviceId);
647 }
648 }
649
650 public static class GroupChecker implements Runnable {
Ray Milkey9c9cde42018-01-12 14:22:06 -0800651 final Logger log = getLogger(getClass());
Yi Tsengef19de12017-04-24 11:33:05 -0700652 private Ofdpa2GroupHandler groupHandler;
653
654 public GroupChecker(Ofdpa2GroupHandler groupHandler) {
655 this.groupHandler = groupHandler;
656 }
657
658 @Override
659 public void run() {
Pier Luigi07532ab2018-01-12 16:03:49 +0100660 // GroupChecker execution needs to be protected
661 // from unhandled exceptions
662 try {
Saurav Das2f2c9d02018-04-07 16:51:09 -0700663 if (groupHandler.pendingGroups.size() != 0) {
664 log.debug("pending groups being checked: {}",
665 groupHandler.pendingGroups.asMap().keySet());
Pier Luigi07532ab2018-01-12 16:03:49 +0100666 }
Saurav Das2f2c9d02018-04-07 16:51:09 -0700667 if (groupHandler.pendingAddNextObjectives.size() != 0) {
Pier Luigi07532ab2018-01-12 16:03:49 +0100668 log.debug("pending add-next-obj being checked: {}",
Saurav Das2f2c9d02018-04-07 16:51:09 -0700669 groupHandler.pendingAddNextObjectives.asMap().keySet());
Pier Luigi07532ab2018-01-12 16:03:49 +0100670 }
Saurav Das2f2c9d02018-04-07 16:51:09 -0700671 if (groupHandler.pendingRemoveNextObjectives.size() != 0) {
672 log.debug("pending remove-next-obj being checked: {}",
673 groupHandler.pendingRemoveNextObjectives.asMap().values());
674 }
675 if (groupHandler.pendingUpdateNextObjectives.size() != 0) {
676 log.debug("pending update-next-obj being checked: {}",
677 groupHandler.pendingUpdateNextObjectives.keySet());
678 }
679
680 Set<GroupKey> keys = groupHandler.pendingGroups.asMap().keySet()
681 .stream()
682 .filter(key -> groupHandler.groupService
683 .getGroup(groupHandler.deviceId, key) != null)
Pier Luigi07532ab2018-01-12 16:03:49 +0100684 .collect(Collectors.toSet());
Saurav Das2f2c9d02018-04-07 16:51:09 -0700685 Set<GroupKey> otherkeys = groupHandler.pendingAddNextObjectives
686 .asMap().keySet().stream()
687 .filter(otherkey -> groupHandler.groupService
688 .getGroup(groupHandler.deviceId, otherkey) != null)
Pier Luigi07532ab2018-01-12 16:03:49 +0100689 .collect(Collectors.toSet());
690 keys.addAll(otherkeys);
Pier Luigi07532ab2018-01-12 16:03:49 +0100691 keys.forEach(key -> groupHandler.processPendingAddGroupsOrNextObjs(key, false));
Saurav Das2f2c9d02018-04-07 16:51:09 -0700692
693 keys = groupHandler.pendingUpdateNextObjectives.keySet()
694 .stream()
695 .filter(key -> groupHandler.groupService
696 .getGroup(groupHandler.deviceId, key) != null)
697 .collect(Collectors.toSet());
698 keys.forEach(key -> groupHandler.processPendingUpdateNextObjs(key));
699
700 Set<GroupKey> k = Sets.newHashSet();
701 groupHandler.pendingRemoveNextObjectives
702 .asMap().values().stream().forEach(keylist -> {
703 k.addAll(keylist.stream()
704 .filter(key -> groupHandler.groupService
705 .getGroup(groupHandler.deviceId, key) == null)
706 .collect(Collectors.toSet()));
707 });
708 k.forEach(key -> groupHandler.processPendingRemoveNextObjs(key));
709
Pier Luigi07532ab2018-01-12 16:03:49 +0100710 } catch (Exception exception) {
711 // Just log. It is safe for now.
712 log.warn("Uncaught exception is detected: {}", exception.getMessage());
713 log.debug("Uncaught exception is detected (full stack trace): ", exception);
714 }
Yi Tsengef19de12017-04-24 11:33:05 -0700715 }
716 }
Andrea Campanella7c977b92018-05-30 21:39:45 -0700717
718 /**
719 * Helper method to decide whether L2 Interface group or L2 Unfiltered group needs to be created.
720 * L2 Unfiltered group will be created if meta has VlanIdCriterion with VlanId.ANY, and
721 * treatment has set Vlan ID action.
722 *
723 * @param treatment treatment passed in by the application as part of the nextObjective
724 * @param meta metadata passed in by the application as part of the nextObjective
725 * @return true if L2 Unfiltered group needs to be created, false otherwise.
726 */
727 public static boolean createUnfiltered(TrafficTreatment treatment, TrafficSelector meta) {
728 if (meta == null || treatment == null) {
729 return false;
730 }
731 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
732 if (vlanIdCriterion == null || !vlanIdCriterion.vlanId().equals(VlanId.ANY)) {
733 return false;
734 }
735
736 return treatment.allInstructions().stream()
737 .filter(i -> (i.type() == Instruction.Type.L2MODIFICATION
738 && ((L2ModificationInstruction) i).subtype() == L2ModificationInstruction.L2SubType.VLAN_ID))
739 .count() == 1;
740 }
741
742 /**
743 * Returns a hash as the L3 Unicast Group Key.
744 *
745 * Keep the lower 6-bit for port since port number usually smaller than 64.
746 * Hash other information into remaining 28 bits.
747 *
748 * @param deviceId Device ID
749 * @param vlanId vlan ID
750 * @param portNumber Port number
751 * @return L3 unicast group key
752 */
753 public static int doubleVlanL3UnicastGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
754 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
755 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
756 int hash = Objects.hash(deviceId, portHigherBits, vlanId);
757 return L3_UNICAST_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
758 }
759
760 public static int doubleVlanL3UnicastGroupId(VlanId vlanId, long portNum) {
761 // <4bits-2><1bit-1><12bits-vlanId><15bits-portId>
762 return L3_UNICAST_TYPE | 1 << 27 | (vlanId.toShort() << 15) | (int) (portNum & 0x7FFF);
763 }
764
Yi Tsengef19de12017-04-24 11:33:05 -0700765}