blob: 5e48ebf2799bfbd7dc443a371192e8662c33ceea [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;
jayakumarthazhath655b9a82018-10-01 00:51:54 +053080 static final int L2_MULTICAST_TYPE = 0x30000000;
Yi Tsengef19de12017-04-24 11:33:05 -070081
Ray Milkey9c9cde42018-01-12 14:22:06 -080082 static final int TYPE_MASK = 0x0fffffff;
83 static final int SUBTYPE_MASK = 0x00ffffff;
84 static final int TYPE_VLAN_MASK = 0x0000ffff;
Andrea Campanella7c977b92018-05-30 21:39:45 -070085 static final int TYPE_L3UG_DOUBLE_VLAN_MASK = 0x08000000;
Yi Tsengef19de12017-04-24 11:33:05 -070086
Ray Milkey9c9cde42018-01-12 14:22:06 -080087 static final int THREE_BIT_MASK = 0x0fff;
88 static final int FOUR_BIT_MASK = 0xffff;
89 static final int PORT_LEN = 16;
Yi Tsengef19de12017-04-24 11:33:05 -070090
Ray Milkey9c9cde42018-01-12 14:22:06 -080091 static final int PORT_LOWER_BITS_MASK = 0x3f;
92 static final long PORT_HIGHER_BITS_MASK = ~PORT_LOWER_BITS_MASK;
Yi Tsengef19de12017-04-24 11:33:05 -070093
Ray Milkey9c9cde42018-01-12 14:22:06 -080094 static final String HEX_PREFIX = "0x";
95 private static final Logger log = getLogger(OfdpaGroupHandlerUtility.class);
Yi Tsengef19de12017-04-24 11:33:05 -070096
97 private OfdpaGroupHandlerUtility() {
98 // Utility classes should not have a public or default constructor.
99 }
100
101 /**
102 * Returns the outport in a traffic treatment.
103 *
104 * @param tt the treatment
105 * @return the PortNumber for the outport or null
106 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800107 static PortNumber readOutPortFromTreatment(TrafficTreatment tt) {
Yi Tsengef19de12017-04-24 11:33:05 -0700108 for (Instruction ins : tt.allInstructions()) {
109 if (ins.type() == Instruction.Type.OUTPUT) {
110 return ((Instructions.OutputInstruction) ins).port();
111 }
112 }
113 return null;
114 }
115
116 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700117 * Returns the MPLS label-id in a traffic treatment.
118 *
119 * @param tt the traffic treatment
120 * @return an integer representing the MPLS label-id, or -1 if not found
121 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800122 static int readLabelFromTreatment(TrafficTreatment tt) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700123 for (Instruction ins : tt.allInstructions()) {
124 if (ins.type() == Instruction.Type.L2MODIFICATION) {
125 L2ModificationInstruction insl2 = (L2ModificationInstruction) ins;
126 if (insl2.subtype() == L2SubType.MPLS_LABEL) {
127 return ((L2ModificationInstruction.ModMplsLabelInstruction) insl2)
128 .label().id();
129 }
130 }
131 }
132 return -1;
133 }
134
135 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700136 * Helper enum to handle the different MPLS group
137 * types.
138 */
139 public enum OfdpaMplsGroupSubType {
140 MPLS_INTF((short) 0),
141 L2_VPN((short) 1),
142 L3_VPN((short) 2),
143 MPLS_TUNNEL_LABEL_1((short) 3),
144 MPLS_TUNNEL_LABEL_2((short) 4),
145 MPLS_SWAP_LABEL((short) 5),
146 MPLS_ECMP((short) 8);
147
148 private short value;
149 public static final int OFDPA_GROUP_TYPE_SHIFT = 28;
150 public static final int OFDPA_MPLS_SUBTYPE_SHIFT = 24;
151
152 OfdpaMplsGroupSubType(short value) {
153 this.value = value;
154 }
155
156 /**
157 * Gets the value as an short.
158 *
159 * @return the value as an short
160 */
161 public short getValue() {
162 return this.value;
163 }
164
165 }
166
167 /**
168 * Creates MPLS Label group id given a sub type and
169 * the index.
170 *
171 * @param subType the MPLS Label group sub type
172 * @param index the index of the group
173 * @return the OFDPA group id
174 */
175 public static Integer makeMplsLabelGroupId(OfdpaMplsGroupSubType subType, int index) {
176 index = index & 0x00FFFFFF;
177 return index | (9 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
178 }
179
180 /**
181 * Creates MPLS Forwarding group id given a sub type and
182 * the index.
183 *
184 * @param subType the MPLS forwarding group sub type
185 * @param index the index of the group
186 * @return the OFDPA group id
187 */
188 public static Integer makeMplsForwardingGroupId(OfdpaMplsGroupSubType subType, int index) {
189 index = index & 0x00FFFFFF;
190 return index | (10 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
191 }
192
193 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700194 * Returns the set of existing output ports in the group represented by
195 * allActiveKeys.
Yi Tsengef19de12017-04-24 11:33:05 -0700196 *
197 * @param allActiveKeys list of group key chain
198 * @param groupService the group service to get group information
199 * @param deviceId the device id to get group
200 * @return a set of output port from the list of group key chain
201 */
202 public static Set<PortNumber> getExistingOutputPorts(List<Deque<GroupKey>> allActiveKeys,
203 GroupService groupService,
204 DeviceId deviceId) {
205 Set<PortNumber> existingPorts = Sets.newHashSet();
206
207 allActiveKeys.forEach(keyChain -> {
208 GroupKey ifaceGroupKey = keyChain.peekLast();
209 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
210 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
211 ifaceGroup.buckets().buckets().forEach(bucket -> {
212 PortNumber portNumber = readOutPortFromTreatment(bucket.treatment());
213 if (portNumber != null) {
214 existingPorts.add(portNumber);
215 }
216 });
217 }
218 });
219 return existingPorts;
220 }
221
222 /**
Saurav Dasceccf242017-08-03 18:30:35 -0700223 * Returns a list of all indices in the allActiveKeys list (that represents
224 * a group) if the list element (a bucket or group-chain) has treatments
225 * that match the given outport and label.
Saurav Das7bcbe702017-06-13 15:35:54 -0700226 *
227 * @param allActiveKeys the representation of the group
228 * @param groupService groups service for querying group information
229 * @param deviceId the device id for the device that contains the group
230 * @param portToMatch the port to match in the group buckets
231 * @param labelToMatch the MPLS label-id to match in the group buckets
Saurav Dasceccf242017-08-03 18:30:35 -0700232 * @return a list of indexes in the allActiveKeys list where the list element
233 * has treatments that match the given portToMatch and labelToMatch.
234 * Could be empty if no list elements were found to match the given
235 * port and label.
Saurav Das7bcbe702017-06-13 15:35:54 -0700236 */
Saurav Dasceccf242017-08-03 18:30:35 -0700237 public static List<Integer> existingPortAndLabel(
238 List<Deque<GroupKey>> allActiveKeys,
Saurav Das7bcbe702017-06-13 15:35:54 -0700239 GroupService groupService,
240 DeviceId deviceId,
241 PortNumber portToMatch,
242 int labelToMatch) {
Saurav Dasceccf242017-08-03 18:30:35 -0700243 List<Integer> indices = new ArrayList<>();
244 int index = 0;
Saurav Das7bcbe702017-06-13 15:35:54 -0700245 for (Deque<GroupKey> keyChain : allActiveKeys) {
246 GroupKey ifaceGroupKey = keyChain.peekLast();
247 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
248 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
249 PortNumber portNumber = readOutPortFromTreatment(
250 ifaceGroup.buckets().buckets().iterator().next().treatment());
251 if (portNumber != null && portNumber.equals(portToMatch)) {
252 // check for label in the 2nd group of this chain
253 GroupKey secondKey = (GroupKey) keyChain.toArray()[1];
254 Group secondGroup = groupService.getGroup(deviceId, secondKey);
255 if (secondGroup != null &&
256 !secondGroup.buckets().buckets().isEmpty()) {
257 int label = readLabelFromTreatment(
258 secondGroup.buckets().buckets()
259 .iterator().next().treatment());
260 if (label == labelToMatch) {
Saurav Dasceccf242017-08-03 18:30:35 -0700261 indices.add(index);
Saurav Das7bcbe702017-06-13 15:35:54 -0700262 }
263 }
264 }
265 }
Saurav Dasceccf242017-08-03 18:30:35 -0700266 index++;
Saurav Das7bcbe702017-06-13 15:35:54 -0700267 }
268
Saurav Dasceccf242017-08-03 18:30:35 -0700269 return indices;
Saurav Das7bcbe702017-06-13 15:35:54 -0700270 }
271
Pier Luigiec6ac422018-01-29 10:30:59 +0100272 /**
273 * Get indices to remove comparing next group with next objective.
274 *
275 * @param allActiveKeys the representation of the group
276 * @param nextObjective the next objective to verify
277 * @param groupService groups service for querying group information
278 * @param deviceId the device id for the device that contains the group
279 * @return a list of indexes in the allActiveKeys to remove.
280 */
281 public static List<Integer> indicesToRemoveFromNextGroup(List<Deque<GroupKey>> allActiveKeys,
282 NextObjective nextObjective,
283 GroupService groupService,
284 DeviceId deviceId) {
285 List<Integer> indicesToRemove = Lists.newArrayList();
286 int index = 0;
287 // Iterate over the chain in the next data
288 for (Deque<GroupKey> keyChain : allActiveKeys) {
289 // Valid chain should have at least two elements
290 if (keyChain.size() >= 2) {
291 // Get last group (l2if) and retrieve port number
292 GroupKey ifaceGroupKey = keyChain.peekLast();
293 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
294 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
295 PortNumber portNumber = readOutPortFromTreatment(
296 ifaceGroup.buckets().buckets().iterator().next().treatment());
297 // If there is not a port number continue
298 if (portNumber != null) {
299 // check for label in the 2nd group of this chain
300 GroupKey secondKey = (GroupKey) keyChain.toArray()[1];
301 Group secondGroup = groupService.getGroup(deviceId, secondKey);
302 // If there is not a second group or there are no buckets continue
303 if (secondGroup != null && !secondGroup.buckets().buckets().isEmpty()) {
304 // Get label or -1
305 int label = readLabelFromTreatment(
306 secondGroup.buckets().buckets()
307 .iterator().next().treatment());
308 // Iterate over the next treatments looking for the port and the label
309 boolean matches = false;
310 for (TrafficTreatment t : nextObjective.next()) {
311 PortNumber tPort = readOutPortFromTreatment(t);
312 int tLabel = readLabelFromTreatment(t);
313 if (tPort != null && tPort.equals(portNumber) && tLabel == label) {
314 // We found it, exit
315 matches = true;
316 break;
317 }
318 }
319 // Not found, we have to remove it
320 if (!matches) {
321 indicesToRemove.add(index);
322 }
323 }
324 }
325 }
326 }
327 index++;
328 }
329 return indicesToRemove;
330 }
331
Saurav Das7bcbe702017-06-13 15:35:54 -0700332 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700333 * The purpose of this function is to verify if the hashed next
334 * objective is supported by the current pipeline.
335 *
336 * @param nextObjective the hashed objective to verify
337 * @return true if the hashed objective is supported. Otherwise false.
338 */
339 public static boolean verifyHashedNextObjective(NextObjective nextObjective) {
340 // if it is not hashed, there is something wrong;
341 if (nextObjective.type() != HASHED) {
342 return false;
343 }
344 // The case non supported is the MPLS-ECMP. For now, we try
345 // to create a MPLS-ECMP for the transport of a VPWS. The
346 // necessary info are contained in the meta selector. In particular
347 // we are looking for the case of BoS==False;
348 TrafficSelector metaSelector = nextObjective.meta();
349 if (metaSelector != null && isNotMplsBos(metaSelector)) {
350 return false;
351 }
352
353 return true;
354 }
355
356 /**
357 * Generates a list of group buckets from given list of group information
358 * and group bucket type.
359 *
360 * @param groupInfos a list of group information
361 * @param bucketType group bucket type
362 * @return list of group bucket generate from group information
363 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800364 static List<GroupBucket> generateNextGroupBuckets(List<GroupInfo> groupInfos,
Yi Tsengef19de12017-04-24 11:33:05 -0700365 GroupDescription.Type bucketType) {
366 List<GroupBucket> newBuckets = Lists.newArrayList();
367
368 groupInfos.forEach(groupInfo -> {
369 GroupDescription groupDesc = groupInfo.nextGroupDesc();
370 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
371 treatmentBuilder.group(new GroupId(groupDesc.givenGroupId()));
372 GroupBucket newBucket = null;
373 switch (bucketType) {
374 case ALL:
375 newBucket =
376 DefaultGroupBucket.createAllGroupBucket(treatmentBuilder.build());
377 break;
378 case INDIRECT:
379 newBucket =
380 DefaultGroupBucket.createIndirectGroupBucket(treatmentBuilder.build());
381 break;
382 case SELECT:
383 newBucket =
384 DefaultGroupBucket.createSelectGroupBucket(treatmentBuilder.build());
385 break;
386 case FAILOVER:
387 // TODO: support failover bucket type
388 default:
389 log.warn("Unknown bucket type: {}", bucketType);
390 break;
391 }
392
393 if (newBucket != null) {
394 newBuckets.add(newBucket);
395 }
396
397 });
398
399 return ImmutableList.copyOf(newBuckets);
400 }
401
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +0900402 static List<GroupBucket> createL3MulticastBucket(List<GroupInfo> groupInfos) {
403 List<GroupBucket> l3McastBuckets = new ArrayList<>();
404 // For each inner group
405 groupInfos.forEach(groupInfo -> {
406 // Points to L3 interface group if there is one.
407 // Otherwise points to L2 interface group directly.
408 GroupDescription nextGroupDesc = (groupInfo.nextGroupDesc() != null) ?
409 groupInfo.nextGroupDesc() : groupInfo.innerMostGroupDesc();
410 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
411 ttb.group(new GroupId(nextGroupDesc.givenGroupId()));
412 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
413 l3McastBuckets.add(abucket);
414 });
415 // Done return the new list of buckets
416 return l3McastBuckets;
417 }
418
419 static Group retrieveTopLevelGroup(List<Deque<GroupKey>> allActiveKeys,
420 DeviceId deviceId,
421 GroupService groupService,
422 int nextid) {
423 GroupKey topLevelGroupKey;
424 if (!allActiveKeys.isEmpty()) {
425 topLevelGroupKey = allActiveKeys.get(0).peekFirst();
426 } else {
427 log.warn("Could not determine top level group while processing"
428 + "next:{} in dev:{}", nextid, deviceId);
429 return null;
430 }
431 Group topGroup = groupService.getGroup(deviceId, topLevelGroupKey);
432 if (topGroup == null) {
433 log.warn("Could not find top level group while processing "
434 + "next:{} in dev:{}", nextid, deviceId);
435 }
436 return topGroup;
437 }
438
Yi Tsengef19de12017-04-24 11:33:05 -0700439 /**
440 * Extracts VlanId from given group ID.
441 *
442 * @param groupId the group ID
443 * @return vlan id of the group
444 */
445 public static VlanId extractVlanIdFromGroupId(int groupId) {
446 // Extract the 9th to 20th bit from group id as vlan id.
447 short vlanId = (short) ((groupId & 0x0fff0000) >> 16);
448 return VlanId.vlanId(vlanId);
449 }
450
451 public static GroupKey l2FloodGroupKey(VlanId vlanId, DeviceId deviceId) {
452 int hash = Objects.hash(deviceId, vlanId);
453 hash = L2_FLOOD_TYPE | TYPE_MASK & hash;
454 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
455 }
456
jayakumarthazhath655b9a82018-10-01 00:51:54 +0530457 public static GroupKey l2MulticastGroupKey(VlanId vlanId, DeviceId deviceId) {
458 int hash = Objects.hash(deviceId, vlanId);
459 hash = L2_MULTICAST_TYPE | TYPE_MASK & hash;
460 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
461 }
462
Yi Tsengef19de12017-04-24 11:33:05 -0700463 public static int l2GroupId(VlanId vlanId, long portNum) {
464 return L2_INTERFACE_TYPE | (vlanId.toShort() << 16) | (int) portNum;
465 }
466
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700467 public static int l2UnfilteredGroupId(long portNum) {
468 return L2_UNFILTERED_TYPE | (int) portNum;
469 }
470
Yi Tsengef19de12017-04-24 11:33:05 -0700471 /**
472 * Returns a hash as the L2 Interface Group Key.
473 *
474 * Keep the lower 6-bit for port since port number usually smaller than 64.
475 * Hash other information into remaining 28 bits.
476 *
477 * @param deviceId Device ID
478 * @param vlanId VLAN ID
479 * @param portNumber Port number
480 * @return L2 interface group key
481 */
482 public static int l2InterfaceGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
483 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
484 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
485 int hash = Objects.hash(deviceId, vlanId, portHigherBits);
486 return L2_INTERFACE_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
487 }
488
489 /**
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700490 * Returns a hash as the L2 Unfiltered Interface Group Key.
491 *
492 * Keep the lower 6-bit for port since port number usually smaller than 64.
493 * Hash other information into remaining 28 bits.
494 *
495 * @param deviceId Device ID
496 * @param portNumber Port number
497 * @return L2 unfiltered interface group key
498 */
499 public static int l2UnfilteredGroupKey(DeviceId deviceId, long portNumber) {
500 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
501 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
502 int hash = Objects.hash(deviceId, portHigherBits);
503 return L2_UNFILTERED_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
504 }
505
506 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700507 * Utility class for moving group information around.
508 *
509 * Example: Suppose we are trying to create a group-chain A-B-C-D, where
510 * A is the top level group, and D is the inner-most group, typically L2 Interface.
511 * The innerMostGroupDesc is always D. At various stages of the creation
512 * process the nextGroupDesc may be C or B. The nextGroupDesc exists to
513 * inform the referencing group about which group it needs to point to,
514 * and wait for. In some cases the group chain may simply be A-B. In this case,
515 * both innerMostGroupDesc and nextGroupDesc will be B.
516 */
517 public static class GroupInfo {
518 /**
519 * Description of the inner-most group of the group chain.
520 * It is always an L2 interface group.
521 */
522 private GroupDescription innerMostGroupDesc;
523
524 /**
525 * Description of the next group in the group chain.
526 * It can be L2 interface, L3 interface, L3 unicast, L3 VPN group.
527 * It is possible that nextGroupDesc is the same as the innerMostGroup.
528 */
529 private GroupDescription nextGroupDesc;
530
531 GroupInfo(GroupDescription innerMostGroupDesc, GroupDescription nextGroupDesc) {
532 this.innerMostGroupDesc = innerMostGroupDesc;
533 this.nextGroupDesc = nextGroupDesc;
534 }
535
536 /**
537 * Getter for innerMostGroupDesc.
538 *
539 * @return the inner most group description
540 */
541 public GroupDescription innerMostGroupDesc() {
542 return innerMostGroupDesc;
543 }
544
545 /**
546 * Getter for the next group description.
547 *
548 * @return the next group description
549 */
550 public GroupDescription nextGroupDesc() {
551 return nextGroupDesc;
552 }
553
554 /**
555 * Setter of nextGroupDesc.
556 *
557 * @param nextGroupDesc the given value to set
558 */
559 public void nextGroupDesc(GroupDescription nextGroupDesc) {
560 this.nextGroupDesc = nextGroupDesc;
561 }
562 }
563
564 /**
565 * Represents an entire group-chain that implements a Next-Objective from
566 * the application. The objective is represented as a list of deques, where
567 * each deque is a separate chain of groups.
568 * <p>
569 * For example, an ECMP group with 3 buckets, where each bucket points to
570 * a group chain of L3 Unicast and L2 interface groups will look like this:
571 * <ul>
572 * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
573 * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
574 * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
575 * </ul>
576 * where the first element of each deque is the same, representing the
577 * top level ECMP group, while every other element represents a unique groupKey.
578 * <p>
579 * Also includes information about the next objective that
580 * resulted in these group-chains.
581 *
582 */
583 public static class OfdpaNextGroup implements NextGroup {
584 private final NextObjective nextObj;
585 private final List<Deque<GroupKey>> gkeys;
586
587 public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
588 this.nextObj = nextObj;
589 this.gkeys = gkeys;
590 }
591
592 public NextObjective nextObjective() {
593 return nextObj;
594 }
595
Saurav Dasc88d4662017-05-15 15:34:25 -0700596 public List<Deque<GroupKey>> allKeys() {
597 return gkeys;
598 }
599
Yi Tsengef19de12017-04-24 11:33:05 -0700600 @Override
601 public byte[] data() {
602 return Ofdpa2Pipeline.appKryo.serialize(gkeys);
603 }
604 }
605
606 /**
607 * Represents a group element that is part of a chain of groups.
608 * Stores enough information to create a Group Description to add the group
609 * to the switch by requesting the Group Service. Objects instantiating this
610 * class are meant to be temporary and live as long as it is needed to wait for
611 * referenced groups in the group chain to be created.
612 */
613 public static class GroupChainElem {
614 private GroupDescription groupDescription;
615 private AtomicInteger waitOnGroups;
616 private boolean addBucketToGroup;
617 private DeviceId deviceId;
618
619 public GroupChainElem(GroupDescription groupDescription, int waitOnGroups,
620 boolean addBucketToGroup, DeviceId deviceId) {
621 this.groupDescription = groupDescription;
622 this.waitOnGroups = new AtomicInteger(waitOnGroups);
623 this.addBucketToGroup = addBucketToGroup;
624 this.deviceId = deviceId;
625 }
626
627 /**
628 * This method atomically decrements the counter for the number of
629 * groups this GroupChainElement is waiting on, for notifications from
630 * the Group Service. When this method returns a value of 0, this
631 * GroupChainElement is ready to be processed.
632 *
633 * @return integer indication of the number of notifications being waited on
634 */
635 int decrementAndGetGroupsWaitedOn() {
636 return waitOnGroups.decrementAndGet();
637 }
638
639 public GroupDescription groupDescription() {
640 return groupDescription;
641 }
642
643 public boolean addBucketToGroup() {
644 return addBucketToGroup;
645 }
646
647 @Override
648 public String toString() {
649 return (Integer.toHexString(groupDescription.givenGroupId()) +
650 " groupKey: " + groupDescription.appCookie() +
651 " waiting-on-groups: " + waitOnGroups.get() +
652 " addBucketToGroup: " + addBucketToGroup +
653 " device: " + deviceId);
654 }
655 }
656
657 public static class GroupChecker implements Runnable {
Ray Milkey9c9cde42018-01-12 14:22:06 -0800658 final Logger log = getLogger(getClass());
Yi Tsengef19de12017-04-24 11:33:05 -0700659 private Ofdpa2GroupHandler groupHandler;
660
661 public GroupChecker(Ofdpa2GroupHandler groupHandler) {
662 this.groupHandler = groupHandler;
663 }
664
665 @Override
666 public void run() {
Pier Luigi07532ab2018-01-12 16:03:49 +0100667 // GroupChecker execution needs to be protected
668 // from unhandled exceptions
669 try {
Saurav Das2f2c9d02018-04-07 16:51:09 -0700670 if (groupHandler.pendingGroups.size() != 0) {
671 log.debug("pending groups being checked: {}",
672 groupHandler.pendingGroups.asMap().keySet());
Pier Luigi07532ab2018-01-12 16:03:49 +0100673 }
Saurav Das2f2c9d02018-04-07 16:51:09 -0700674 if (groupHandler.pendingAddNextObjectives.size() != 0) {
Pier Luigi07532ab2018-01-12 16:03:49 +0100675 log.debug("pending add-next-obj being checked: {}",
Saurav Das2f2c9d02018-04-07 16:51:09 -0700676 groupHandler.pendingAddNextObjectives.asMap().keySet());
Pier Luigi07532ab2018-01-12 16:03:49 +0100677 }
Saurav Das2f2c9d02018-04-07 16:51:09 -0700678 if (groupHandler.pendingRemoveNextObjectives.size() != 0) {
679 log.debug("pending remove-next-obj being checked: {}",
680 groupHandler.pendingRemoveNextObjectives.asMap().values());
681 }
682 if (groupHandler.pendingUpdateNextObjectives.size() != 0) {
683 log.debug("pending update-next-obj being checked: {}",
684 groupHandler.pendingUpdateNextObjectives.keySet());
685 }
686
687 Set<GroupKey> keys = groupHandler.pendingGroups.asMap().keySet()
688 .stream()
689 .filter(key -> groupHandler.groupService
690 .getGroup(groupHandler.deviceId, key) != null)
Pier Luigi07532ab2018-01-12 16:03:49 +0100691 .collect(Collectors.toSet());
Saurav Das2f2c9d02018-04-07 16:51:09 -0700692 Set<GroupKey> otherkeys = groupHandler.pendingAddNextObjectives
693 .asMap().keySet().stream()
694 .filter(otherkey -> groupHandler.groupService
695 .getGroup(groupHandler.deviceId, otherkey) != null)
Pier Luigi07532ab2018-01-12 16:03:49 +0100696 .collect(Collectors.toSet());
697 keys.addAll(otherkeys);
Pier Luigi07532ab2018-01-12 16:03:49 +0100698 keys.forEach(key -> groupHandler.processPendingAddGroupsOrNextObjs(key, false));
Saurav Das2f2c9d02018-04-07 16:51:09 -0700699
700 keys = groupHandler.pendingUpdateNextObjectives.keySet()
701 .stream()
702 .filter(key -> groupHandler.groupService
703 .getGroup(groupHandler.deviceId, key) != null)
704 .collect(Collectors.toSet());
705 keys.forEach(key -> groupHandler.processPendingUpdateNextObjs(key));
706
707 Set<GroupKey> k = Sets.newHashSet();
708 groupHandler.pendingRemoveNextObjectives
709 .asMap().values().stream().forEach(keylist -> {
710 k.addAll(keylist.stream()
711 .filter(key -> groupHandler.groupService
712 .getGroup(groupHandler.deviceId, key) == null)
713 .collect(Collectors.toSet()));
714 });
715 k.forEach(key -> groupHandler.processPendingRemoveNextObjs(key));
716
Pier Luigi07532ab2018-01-12 16:03:49 +0100717 } catch (Exception exception) {
718 // Just log. It is safe for now.
719 log.warn("Uncaught exception is detected: {}", exception.getMessage());
720 log.debug("Uncaught exception is detected (full stack trace): ", exception);
721 }
Yi Tsengef19de12017-04-24 11:33:05 -0700722 }
723 }
Andrea Campanella7c977b92018-05-30 21:39:45 -0700724
725 /**
726 * Helper method to decide whether L2 Interface group or L2 Unfiltered group needs to be created.
727 * L2 Unfiltered group will be created if meta has VlanIdCriterion with VlanId.ANY, and
728 * treatment has set Vlan ID action.
729 *
730 * @param treatment treatment passed in by the application as part of the nextObjective
731 * @param meta metadata passed in by the application as part of the nextObjective
732 * @return true if L2 Unfiltered group needs to be created, false otherwise.
733 */
734 public static boolean createUnfiltered(TrafficTreatment treatment, TrafficSelector meta) {
735 if (meta == null || treatment == null) {
736 return false;
737 }
738 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
739 if (vlanIdCriterion == null || !vlanIdCriterion.vlanId().equals(VlanId.ANY)) {
740 return false;
741 }
742
743 return treatment.allInstructions().stream()
744 .filter(i -> (i.type() == Instruction.Type.L2MODIFICATION
745 && ((L2ModificationInstruction) i).subtype() == L2ModificationInstruction.L2SubType.VLAN_ID))
746 .count() == 1;
747 }
748
749 /**
750 * Returns a hash as the L3 Unicast Group Key.
751 *
752 * Keep the lower 6-bit for port since port number usually smaller than 64.
753 * Hash other information into remaining 28 bits.
754 *
755 * @param deviceId Device ID
756 * @param vlanId vlan ID
757 * @param portNumber Port number
758 * @return L3 unicast group key
759 */
760 public static int doubleVlanL3UnicastGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
761 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
762 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
763 int hash = Objects.hash(deviceId, portHigherBits, vlanId);
764 return L3_UNICAST_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
765 }
766
767 public static int doubleVlanL3UnicastGroupId(VlanId vlanId, long portNum) {
768 // <4bits-2><1bit-1><12bits-vlanId><15bits-portId>
769 return L3_UNICAST_TYPE | 1 << 27 | (vlanId.toShort() << 15) | (int) (portNum & 0x7FFF);
770 }
771
Yi Tsengef19de12017-04-24 11:33:05 -0700772}