blob: afab73f3321075ba9ddae698f0ae9f9507aac64a [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
pier9469f3e2019-04-17 17:05:08 +020054import static org.onosproject.driver.pipeline.ofdpa.OfdpaPipelineUtility.isNotMplsBos;
Yi Tsengef19de12017-04-24 11:33:05 -070055import 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;
Charles Chan367c1c12018-10-19 16:23:28 -070081 static final int L2_LB_TYPE = 0xc0000000;
Yi Tsengef19de12017-04-24 11:33:05 -070082
Ray Milkey9c9cde42018-01-12 14:22:06 -080083 static final int TYPE_MASK = 0x0fffffff;
84 static final int SUBTYPE_MASK = 0x00ffffff;
85 static final int TYPE_VLAN_MASK = 0x0000ffff;
Andrea Campanella7c977b92018-05-30 21:39:45 -070086 static final int TYPE_L3UG_DOUBLE_VLAN_MASK = 0x08000000;
Yi Tsengef19de12017-04-24 11:33:05 -070087
Charles Chan367c1c12018-10-19 16:23:28 -070088 static final int THREE_NIBBLE_MASK = 0x0fff;
89 static final int FOUR_NIBBLE_MASK = 0xffff;
Ray Milkey9c9cde42018-01-12 14:22:06 -080090 static final int PORT_LEN = 16;
Yi Tsengef19de12017-04-24 11:33:05 -070091
Ray Milkey9c9cde42018-01-12 14:22:06 -080092 static final int PORT_LOWER_BITS_MASK = 0x3f;
93 static final long PORT_HIGHER_BITS_MASK = ~PORT_LOWER_BITS_MASK;
Yi Tsengef19de12017-04-24 11:33:05 -070094
Ray Milkey9c9cde42018-01-12 14:22:06 -080095 static final String HEX_PREFIX = "0x";
96 private static final Logger log = getLogger(OfdpaGroupHandlerUtility.class);
Yi Tsengef19de12017-04-24 11:33:05 -070097
98 private OfdpaGroupHandlerUtility() {
99 // Utility classes should not have a public or default constructor.
100 }
101
102 /**
103 * Returns the outport in a traffic treatment.
104 *
105 * @param tt the treatment
106 * @return the PortNumber for the outport or null
107 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800108 static PortNumber readOutPortFromTreatment(TrafficTreatment tt) {
Yi Tsengef19de12017-04-24 11:33:05 -0700109 for (Instruction ins : tt.allInstructions()) {
110 if (ins.type() == Instruction.Type.OUTPUT) {
111 return ((Instructions.OutputInstruction) ins).port();
112 }
113 }
114 return null;
115 }
116
117 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700118 * Returns the MPLS label-id in a traffic treatment.
119 *
120 * @param tt the traffic treatment
121 * @return an integer representing the MPLS label-id, or -1 if not found
122 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800123 static int readLabelFromTreatment(TrafficTreatment tt) {
Saurav Das7bcbe702017-06-13 15:35:54 -0700124 for (Instruction ins : tt.allInstructions()) {
125 if (ins.type() == Instruction.Type.L2MODIFICATION) {
126 L2ModificationInstruction insl2 = (L2ModificationInstruction) ins;
127 if (insl2.subtype() == L2SubType.MPLS_LABEL) {
128 return ((L2ModificationInstruction.ModMplsLabelInstruction) insl2)
129 .label().id();
130 }
131 }
132 }
133 return -1;
134 }
135
136 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700137 * Helper enum to handle the different MPLS group
138 * types.
139 */
140 public enum OfdpaMplsGroupSubType {
141 MPLS_INTF((short) 0),
142 L2_VPN((short) 1),
143 L3_VPN((short) 2),
144 MPLS_TUNNEL_LABEL_1((short) 3),
145 MPLS_TUNNEL_LABEL_2((short) 4),
146 MPLS_SWAP_LABEL((short) 5),
147 MPLS_ECMP((short) 8);
148
149 private short value;
150 public static final int OFDPA_GROUP_TYPE_SHIFT = 28;
151 public static final int OFDPA_MPLS_SUBTYPE_SHIFT = 24;
152
153 OfdpaMplsGroupSubType(short value) {
154 this.value = value;
155 }
156
157 /**
158 * Gets the value as an short.
159 *
160 * @return the value as an short
161 */
162 public short getValue() {
163 return this.value;
164 }
165
166 }
167
168 /**
169 * Creates MPLS Label group id given a sub type and
170 * the index.
171 *
172 * @param subType the MPLS Label group sub type
173 * @param index the index of the group
174 * @return the OFDPA group id
175 */
176 public static Integer makeMplsLabelGroupId(OfdpaMplsGroupSubType subType, int index) {
177 index = index & 0x00FFFFFF;
178 return index | (9 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
179 }
180
181 /**
182 * Creates MPLS Forwarding group id given a sub type and
183 * the index.
184 *
185 * @param subType the MPLS forwarding group sub type
186 * @param index the index of the group
187 * @return the OFDPA group id
188 */
189 public static Integer makeMplsForwardingGroupId(OfdpaMplsGroupSubType subType, int index) {
190 index = index & 0x00FFFFFF;
191 return index | (10 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
192 }
193
194 /**
Saurav Das7bcbe702017-06-13 15:35:54 -0700195 * Returns the set of existing output ports in the group represented by
196 * allActiveKeys.
Yi Tsengef19de12017-04-24 11:33:05 -0700197 *
198 * @param allActiveKeys list of group key chain
199 * @param groupService the group service to get group information
200 * @param deviceId the device id to get group
201 * @return a set of output port from the list of group key chain
202 */
203 public static Set<PortNumber> getExistingOutputPorts(List<Deque<GroupKey>> allActiveKeys,
204 GroupService groupService,
205 DeviceId deviceId) {
206 Set<PortNumber> existingPorts = Sets.newHashSet();
207
208 allActiveKeys.forEach(keyChain -> {
209 GroupKey ifaceGroupKey = keyChain.peekLast();
210 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
211 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
212 ifaceGroup.buckets().buckets().forEach(bucket -> {
213 PortNumber portNumber = readOutPortFromTreatment(bucket.treatment());
214 if (portNumber != null) {
215 existingPorts.add(portNumber);
216 }
217 });
218 }
219 });
220 return existingPorts;
221 }
222
223 /**
Saurav Dasceccf242017-08-03 18:30:35 -0700224 * Returns a list of all indices in the allActiveKeys list (that represents
225 * a group) if the list element (a bucket or group-chain) has treatments
226 * that match the given outport and label.
Saurav Das7bcbe702017-06-13 15:35:54 -0700227 *
228 * @param allActiveKeys the representation of the group
229 * @param groupService groups service for querying group information
230 * @param deviceId the device id for the device that contains the group
231 * @param portToMatch the port to match in the group buckets
232 * @param labelToMatch the MPLS label-id to match in the group buckets
Saurav Dasceccf242017-08-03 18:30:35 -0700233 * @return a list of indexes in the allActiveKeys list where the list element
234 * has treatments that match the given portToMatch and labelToMatch.
235 * Could be empty if no list elements were found to match the given
236 * port and label.
Saurav Das7bcbe702017-06-13 15:35:54 -0700237 */
Saurav Dasceccf242017-08-03 18:30:35 -0700238 public static List<Integer> existingPortAndLabel(
239 List<Deque<GroupKey>> allActiveKeys,
Saurav Das7bcbe702017-06-13 15:35:54 -0700240 GroupService groupService,
241 DeviceId deviceId,
242 PortNumber portToMatch,
243 int labelToMatch) {
Saurav Dasceccf242017-08-03 18:30:35 -0700244 List<Integer> indices = new ArrayList<>();
245 int index = 0;
Saurav Das7bcbe702017-06-13 15:35:54 -0700246 for (Deque<GroupKey> keyChain : allActiveKeys) {
247 GroupKey ifaceGroupKey = keyChain.peekLast();
248 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
249 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
250 PortNumber portNumber = readOutPortFromTreatment(
251 ifaceGroup.buckets().buckets().iterator().next().treatment());
252 if (portNumber != null && portNumber.equals(portToMatch)) {
253 // check for label in the 2nd group of this chain
254 GroupKey secondKey = (GroupKey) keyChain.toArray()[1];
255 Group secondGroup = groupService.getGroup(deviceId, secondKey);
256 if (secondGroup != null &&
257 !secondGroup.buckets().buckets().isEmpty()) {
258 int label = readLabelFromTreatment(
259 secondGroup.buckets().buckets()
260 .iterator().next().treatment());
261 if (label == labelToMatch) {
Saurav Dasceccf242017-08-03 18:30:35 -0700262 indices.add(index);
Saurav Das7bcbe702017-06-13 15:35:54 -0700263 }
264 }
265 }
266 }
Saurav Dasceccf242017-08-03 18:30:35 -0700267 index++;
Saurav Das7bcbe702017-06-13 15:35:54 -0700268 }
269
Saurav Dasceccf242017-08-03 18:30:35 -0700270 return indices;
Saurav Das7bcbe702017-06-13 15:35:54 -0700271 }
272
Pier Luigiec6ac422018-01-29 10:30:59 +0100273 /**
274 * Get indices to remove comparing next group with next objective.
275 *
276 * @param allActiveKeys the representation of the group
277 * @param nextObjective the next objective to verify
278 * @param groupService groups service for querying group information
279 * @param deviceId the device id for the device that contains the group
280 * @return a list of indexes in the allActiveKeys to remove.
281 */
282 public static List<Integer> indicesToRemoveFromNextGroup(List<Deque<GroupKey>> allActiveKeys,
283 NextObjective nextObjective,
284 GroupService groupService,
285 DeviceId deviceId) {
286 List<Integer> indicesToRemove = Lists.newArrayList();
287 int index = 0;
288 // Iterate over the chain in the next data
289 for (Deque<GroupKey> keyChain : allActiveKeys) {
290 // Valid chain should have at least two elements
291 if (keyChain.size() >= 2) {
292 // Get last group (l2if) and retrieve port number
293 GroupKey ifaceGroupKey = keyChain.peekLast();
294 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
295 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
296 PortNumber portNumber = readOutPortFromTreatment(
297 ifaceGroup.buckets().buckets().iterator().next().treatment());
298 // If there is not a port number continue
299 if (portNumber != null) {
300 // check for label in the 2nd group of this chain
301 GroupKey secondKey = (GroupKey) keyChain.toArray()[1];
302 Group secondGroup = groupService.getGroup(deviceId, secondKey);
303 // If there is not a second group or there are no buckets continue
304 if (secondGroup != null && !secondGroup.buckets().buckets().isEmpty()) {
305 // Get label or -1
306 int label = readLabelFromTreatment(
307 secondGroup.buckets().buckets()
308 .iterator().next().treatment());
309 // Iterate over the next treatments looking for the port and the label
310 boolean matches = false;
311 for (TrafficTreatment t : nextObjective.next()) {
312 PortNumber tPort = readOutPortFromTreatment(t);
313 int tLabel = readLabelFromTreatment(t);
314 if (tPort != null && tPort.equals(portNumber) && tLabel == label) {
315 // We found it, exit
316 matches = true;
317 break;
318 }
319 }
320 // Not found, we have to remove it
321 if (!matches) {
322 indicesToRemove.add(index);
323 }
324 }
325 }
326 }
327 }
328 index++;
329 }
330 return indicesToRemove;
331 }
332
Saurav Das7bcbe702017-06-13 15:35:54 -0700333 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700334 * The purpose of this function is to verify if the hashed next
335 * objective is supported by the current pipeline.
336 *
337 * @param nextObjective the hashed objective to verify
338 * @return true if the hashed objective is supported. Otherwise false.
339 */
340 public static boolean verifyHashedNextObjective(NextObjective nextObjective) {
341 // if it is not hashed, there is something wrong;
342 if (nextObjective.type() != HASHED) {
343 return false;
344 }
345 // The case non supported is the MPLS-ECMP. For now, we try
346 // to create a MPLS-ECMP for the transport of a VPWS. The
347 // necessary info are contained in the meta selector. In particular
348 // we are looking for the case of BoS==False;
349 TrafficSelector metaSelector = nextObjective.meta();
350 if (metaSelector != null && isNotMplsBos(metaSelector)) {
351 return false;
352 }
353
354 return true;
355 }
356
357 /**
Charles Chan367c1c12018-10-19 16:23:28 -0700358 * Checks if given next objective is L2 hash next objective.
359 *
360 * @param nextObj next objective
361 * @return true if the next objective is L2 hash next objective
362 */
363 static boolean isL2Hash(NextObjective nextObj) {
364 return nextObj.next().stream()
365 .flatMap(t -> t.allInstructions().stream()).allMatch(i -> i.type() == Instruction.Type.OUTPUT);
366 }
367
368 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700369 * Generates a list of group buckets from given list of group information
370 * and group bucket type.
371 *
372 * @param groupInfos a list of group information
373 * @param bucketType group bucket type
374 * @return list of group bucket generate from group information
375 */
Ray Milkey9c9cde42018-01-12 14:22:06 -0800376 static List<GroupBucket> generateNextGroupBuckets(List<GroupInfo> groupInfos,
Yi Tsengef19de12017-04-24 11:33:05 -0700377 GroupDescription.Type bucketType) {
378 List<GroupBucket> newBuckets = Lists.newArrayList();
379
380 groupInfos.forEach(groupInfo -> {
381 GroupDescription groupDesc = groupInfo.nextGroupDesc();
382 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
383 treatmentBuilder.group(new GroupId(groupDesc.givenGroupId()));
384 GroupBucket newBucket = null;
385 switch (bucketType) {
386 case ALL:
387 newBucket =
388 DefaultGroupBucket.createAllGroupBucket(treatmentBuilder.build());
389 break;
390 case INDIRECT:
391 newBucket =
392 DefaultGroupBucket.createIndirectGroupBucket(treatmentBuilder.build());
393 break;
394 case SELECT:
395 newBucket =
396 DefaultGroupBucket.createSelectGroupBucket(treatmentBuilder.build());
397 break;
398 case FAILOVER:
399 // TODO: support failover bucket type
400 default:
401 log.warn("Unknown bucket type: {}", bucketType);
402 break;
403 }
404
405 if (newBucket != null) {
406 newBuckets.add(newBucket);
407 }
408
409 });
410
411 return ImmutableList.copyOf(newBuckets);
412 }
413
Jonghwan Hyunf810a7a2018-02-12 16:43:45 +0900414 static List<GroupBucket> createL3MulticastBucket(List<GroupInfo> groupInfos) {
415 List<GroupBucket> l3McastBuckets = new ArrayList<>();
416 // For each inner group
417 groupInfos.forEach(groupInfo -> {
418 // Points to L3 interface group if there is one.
419 // Otherwise points to L2 interface group directly.
420 GroupDescription nextGroupDesc = (groupInfo.nextGroupDesc() != null) ?
421 groupInfo.nextGroupDesc() : groupInfo.innerMostGroupDesc();
422 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
423 ttb.group(new GroupId(nextGroupDesc.givenGroupId()));
424 GroupBucket abucket = DefaultGroupBucket.createAllGroupBucket(ttb.build());
425 l3McastBuckets.add(abucket);
426 });
427 // Done return the new list of buckets
428 return l3McastBuckets;
429 }
430
431 static Group retrieveTopLevelGroup(List<Deque<GroupKey>> allActiveKeys,
432 DeviceId deviceId,
433 GroupService groupService,
434 int nextid) {
435 GroupKey topLevelGroupKey;
436 if (!allActiveKeys.isEmpty()) {
437 topLevelGroupKey = allActiveKeys.get(0).peekFirst();
438 } else {
439 log.warn("Could not determine top level group while processing"
440 + "next:{} in dev:{}", nextid, deviceId);
441 return null;
442 }
443 Group topGroup = groupService.getGroup(deviceId, topLevelGroupKey);
444 if (topGroup == null) {
445 log.warn("Could not find top level group while processing "
446 + "next:{} in dev:{}", nextid, deviceId);
447 }
448 return topGroup;
449 }
450
Yi Tsengef19de12017-04-24 11:33:05 -0700451 /**
452 * Extracts VlanId from given group ID.
453 *
454 * @param groupId the group ID
455 * @return vlan id of the group
456 */
457 public static VlanId extractVlanIdFromGroupId(int groupId) {
458 // Extract the 9th to 20th bit from group id as vlan id.
459 short vlanId = (short) ((groupId & 0x0fff0000) >> 16);
460 return VlanId.vlanId(vlanId);
461 }
462
463 public static GroupKey l2FloodGroupKey(VlanId vlanId, DeviceId deviceId) {
464 int hash = Objects.hash(deviceId, vlanId);
465 hash = L2_FLOOD_TYPE | TYPE_MASK & hash;
466 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
467 }
468
jayakumarthazhath655b9a82018-10-01 00:51:54 +0530469 public static GroupKey l2MulticastGroupKey(VlanId vlanId, DeviceId deviceId) {
470 int hash = Objects.hash(deviceId, vlanId);
471 hash = L2_MULTICAST_TYPE | TYPE_MASK & hash;
472 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
473 }
474
Yi Tsengef19de12017-04-24 11:33:05 -0700475 public static int l2GroupId(VlanId vlanId, long portNum) {
476 return L2_INTERFACE_TYPE | (vlanId.toShort() << 16) | (int) portNum;
477 }
478
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700479 public static int l2UnfilteredGroupId(long portNum) {
480 return L2_UNFILTERED_TYPE | (int) portNum;
481 }
482
Yi Tsengef19de12017-04-24 11:33:05 -0700483 /**
484 * Returns a hash as the L2 Interface Group Key.
485 *
486 * Keep the lower 6-bit for port since port number usually smaller than 64.
Charles Chan367c1c12018-10-19 16:23:28 -0700487 * Hash other information into remaining 22 bits.
Yi Tsengef19de12017-04-24 11:33:05 -0700488 *
489 * @param deviceId Device ID
490 * @param vlanId VLAN ID
491 * @param portNumber Port number
492 * @return L2 interface group key
493 */
494 public static int l2InterfaceGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
495 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
496 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
497 int hash = Objects.hash(deviceId, vlanId, portHigherBits);
498 return L2_INTERFACE_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
499 }
500
501 /**
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700502 * Returns a hash as the L2 Unfiltered Interface Group Key.
503 *
504 * Keep the lower 6-bit for port since port number usually smaller than 64.
Charles Chan367c1c12018-10-19 16:23:28 -0700505 * Hash other information into remaining 22 bits.
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700506 *
507 * @param deviceId Device ID
508 * @param portNumber Port number
509 * @return L2 unfiltered interface group key
510 */
511 public static int l2UnfilteredGroupKey(DeviceId deviceId, long portNumber) {
512 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
513 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
514 int hash = Objects.hash(deviceId, portHigherBits);
515 return L2_UNFILTERED_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
516 }
517
518 /**
Charles Chan367c1c12018-10-19 16:23:28 -0700519 * Returns a hash as the L2 Hash Group Key.
520 *
521 * Keep the lower 6-bit for port since port number usually smaller than 64.
522 * Hash other information into remaining 28 bits.
523 *
524 * @param deviceId Device ID
525 * @param portNumber Port number
526 * @return L2 hash group key
527 */
528 public static int l2HashGroupKey(DeviceId deviceId, long portNumber) {
529 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
530 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
531 int hash = Objects.hash(deviceId, portHigherBits);
532 return L2_LB_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
533 }
534
535 /**
Yi Tsengef19de12017-04-24 11:33:05 -0700536 * Utility class for moving group information around.
537 *
538 * Example: Suppose we are trying to create a group-chain A-B-C-D, where
539 * A is the top level group, and D is the inner-most group, typically L2 Interface.
540 * The innerMostGroupDesc is always D. At various stages of the creation
541 * process the nextGroupDesc may be C or B. The nextGroupDesc exists to
542 * inform the referencing group about which group it needs to point to,
543 * and wait for. In some cases the group chain may simply be A-B. In this case,
544 * both innerMostGroupDesc and nextGroupDesc will be B.
545 */
546 public static class GroupInfo {
547 /**
548 * Description of the inner-most group of the group chain.
549 * It is always an L2 interface group.
550 */
551 private GroupDescription innerMostGroupDesc;
552
553 /**
554 * Description of the next group in the group chain.
555 * It can be L2 interface, L3 interface, L3 unicast, L3 VPN group.
556 * It is possible that nextGroupDesc is the same as the innerMostGroup.
557 */
558 private GroupDescription nextGroupDesc;
559
560 GroupInfo(GroupDescription innerMostGroupDesc, GroupDescription nextGroupDesc) {
561 this.innerMostGroupDesc = innerMostGroupDesc;
562 this.nextGroupDesc = nextGroupDesc;
563 }
564
565 /**
566 * Getter for innerMostGroupDesc.
567 *
568 * @return the inner most group description
569 */
570 public GroupDescription innerMostGroupDesc() {
571 return innerMostGroupDesc;
572 }
573
574 /**
575 * Getter for the next group description.
576 *
577 * @return the next group description
578 */
579 public GroupDescription nextGroupDesc() {
580 return nextGroupDesc;
581 }
582
583 /**
584 * Setter of nextGroupDesc.
585 *
586 * @param nextGroupDesc the given value to set
587 */
588 public void nextGroupDesc(GroupDescription nextGroupDesc) {
589 this.nextGroupDesc = nextGroupDesc;
590 }
591 }
592
593 /**
594 * Represents an entire group-chain that implements a Next-Objective from
595 * the application. The objective is represented as a list of deques, where
596 * each deque is a separate chain of groups.
597 * <p>
598 * For example, an ECMP group with 3 buckets, where each bucket points to
599 * a group chain of L3 Unicast and L2 interface groups will look like this:
600 * <ul>
601 * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
602 * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
603 * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
604 * </ul>
605 * where the first element of each deque is the same, representing the
606 * top level ECMP group, while every other element represents a unique groupKey.
607 * <p>
608 * Also includes information about the next objective that
609 * resulted in these group-chains.
610 *
611 */
612 public static class OfdpaNextGroup implements NextGroup {
613 private final NextObjective nextObj;
614 private final List<Deque<GroupKey>> gkeys;
615
616 public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
617 this.nextObj = nextObj;
618 this.gkeys = gkeys;
619 }
620
621 public NextObjective nextObjective() {
622 return nextObj;
623 }
624
Saurav Dasc88d4662017-05-15 15:34:25 -0700625 public List<Deque<GroupKey>> allKeys() {
626 return gkeys;
627 }
628
Yi Tsengef19de12017-04-24 11:33:05 -0700629 @Override
630 public byte[] data() {
631 return Ofdpa2Pipeline.appKryo.serialize(gkeys);
632 }
633 }
634
635 /**
636 * Represents a group element that is part of a chain of groups.
637 * Stores enough information to create a Group Description to add the group
638 * to the switch by requesting the Group Service. Objects instantiating this
639 * class are meant to be temporary and live as long as it is needed to wait for
640 * referenced groups in the group chain to be created.
641 */
642 public static class GroupChainElem {
643 private GroupDescription groupDescription;
644 private AtomicInteger waitOnGroups;
645 private boolean addBucketToGroup;
646 private DeviceId deviceId;
647
648 public GroupChainElem(GroupDescription groupDescription, int waitOnGroups,
649 boolean addBucketToGroup, DeviceId deviceId) {
650 this.groupDescription = groupDescription;
651 this.waitOnGroups = new AtomicInteger(waitOnGroups);
652 this.addBucketToGroup = addBucketToGroup;
653 this.deviceId = deviceId;
654 }
655
656 /**
657 * This method atomically decrements the counter for the number of
658 * groups this GroupChainElement is waiting on, for notifications from
659 * the Group Service. When this method returns a value of 0, this
660 * GroupChainElement is ready to be processed.
661 *
662 * @return integer indication of the number of notifications being waited on
663 */
664 int decrementAndGetGroupsWaitedOn() {
665 return waitOnGroups.decrementAndGet();
666 }
667
668 public GroupDescription groupDescription() {
669 return groupDescription;
670 }
671
672 public boolean addBucketToGroup() {
673 return addBucketToGroup;
674 }
675
676 @Override
677 public String toString() {
678 return (Integer.toHexString(groupDescription.givenGroupId()) +
679 " groupKey: " + groupDescription.appCookie() +
680 " waiting-on-groups: " + waitOnGroups.get() +
681 " addBucketToGroup: " + addBucketToGroup +
682 " device: " + deviceId);
683 }
684 }
685
686 public static class GroupChecker implements Runnable {
Ray Milkey9c9cde42018-01-12 14:22:06 -0800687 final Logger log = getLogger(getClass());
Yi Tsengef19de12017-04-24 11:33:05 -0700688 private Ofdpa2GroupHandler groupHandler;
689
690 public GroupChecker(Ofdpa2GroupHandler groupHandler) {
691 this.groupHandler = groupHandler;
692 }
693
694 @Override
695 public void run() {
Pier Luigi07532ab2018-01-12 16:03:49 +0100696 // GroupChecker execution needs to be protected
697 // from unhandled exceptions
698 try {
Saurav Das2f2c9d02018-04-07 16:51:09 -0700699 if (groupHandler.pendingGroups.size() != 0) {
700 log.debug("pending groups being checked: {}",
701 groupHandler.pendingGroups.asMap().keySet());
Pier Luigi07532ab2018-01-12 16:03:49 +0100702 }
Saurav Das2f2c9d02018-04-07 16:51:09 -0700703 if (groupHandler.pendingAddNextObjectives.size() != 0) {
Pier Luigi07532ab2018-01-12 16:03:49 +0100704 log.debug("pending add-next-obj being checked: {}",
Saurav Das2f2c9d02018-04-07 16:51:09 -0700705 groupHandler.pendingAddNextObjectives.asMap().keySet());
Pier Luigi07532ab2018-01-12 16:03:49 +0100706 }
Saurav Das2f2c9d02018-04-07 16:51:09 -0700707 if (groupHandler.pendingRemoveNextObjectives.size() != 0) {
708 log.debug("pending remove-next-obj being checked: {}",
709 groupHandler.pendingRemoveNextObjectives.asMap().values());
710 }
711 if (groupHandler.pendingUpdateNextObjectives.size() != 0) {
712 log.debug("pending update-next-obj being checked: {}",
713 groupHandler.pendingUpdateNextObjectives.keySet());
714 }
715
716 Set<GroupKey> keys = groupHandler.pendingGroups.asMap().keySet()
717 .stream()
718 .filter(key -> groupHandler.groupService
719 .getGroup(groupHandler.deviceId, key) != null)
Pier Luigi07532ab2018-01-12 16:03:49 +0100720 .collect(Collectors.toSet());
Saurav Das2f2c9d02018-04-07 16:51:09 -0700721 Set<GroupKey> otherkeys = groupHandler.pendingAddNextObjectives
722 .asMap().keySet().stream()
723 .filter(otherkey -> groupHandler.groupService
724 .getGroup(groupHandler.deviceId, otherkey) != null)
Pier Luigi07532ab2018-01-12 16:03:49 +0100725 .collect(Collectors.toSet());
726 keys.addAll(otherkeys);
Pier Luigi07532ab2018-01-12 16:03:49 +0100727 keys.forEach(key -> groupHandler.processPendingAddGroupsOrNextObjs(key, false));
Saurav Das2f2c9d02018-04-07 16:51:09 -0700728
729 keys = groupHandler.pendingUpdateNextObjectives.keySet()
730 .stream()
731 .filter(key -> groupHandler.groupService
732 .getGroup(groupHandler.deviceId, key) != null)
733 .collect(Collectors.toSet());
734 keys.forEach(key -> groupHandler.processPendingUpdateNextObjs(key));
735
736 Set<GroupKey> k = Sets.newHashSet();
piera7611732018-12-03 11:25:23 -0800737 groupHandler.pendingRemoveNextObjectives.asMap().values().forEach(keylist -> keylist.stream()
738 .filter(key -> groupHandler.groupService.getGroup(groupHandler.deviceId, key) == null)
739 .forEach(k::add));
Saurav Das2f2c9d02018-04-07 16:51:09 -0700740 k.forEach(key -> groupHandler.processPendingRemoveNextObjs(key));
741
Pier Luigi07532ab2018-01-12 16:03:49 +0100742 } catch (Exception exception) {
743 // Just log. It is safe for now.
744 log.warn("Uncaught exception is detected: {}", exception.getMessage());
745 log.debug("Uncaught exception is detected (full stack trace): ", exception);
746 }
Yi Tsengef19de12017-04-24 11:33:05 -0700747 }
748 }
Andrea Campanella7c977b92018-05-30 21:39:45 -0700749
750 /**
751 * Helper method to decide whether L2 Interface group or L2 Unfiltered group needs to be created.
752 * L2 Unfiltered group will be created if meta has VlanIdCriterion with VlanId.ANY, and
753 * treatment has set Vlan ID action.
754 *
755 * @param treatment treatment passed in by the application as part of the nextObjective
756 * @param meta metadata passed in by the application as part of the nextObjective
757 * @return true if L2 Unfiltered group needs to be created, false otherwise.
758 */
759 public static boolean createUnfiltered(TrafficTreatment treatment, TrafficSelector meta) {
760 if (meta == null || treatment == null) {
761 return false;
762 }
763 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
764 if (vlanIdCriterion == null || !vlanIdCriterion.vlanId().equals(VlanId.ANY)) {
765 return false;
766 }
767
768 return treatment.allInstructions().stream()
769 .filter(i -> (i.type() == Instruction.Type.L2MODIFICATION
770 && ((L2ModificationInstruction) i).subtype() == L2ModificationInstruction.L2SubType.VLAN_ID))
771 .count() == 1;
772 }
773
774 /**
775 * Returns a hash as the L3 Unicast Group Key.
776 *
777 * Keep the lower 6-bit for port since port number usually smaller than 64.
778 * Hash other information into remaining 28 bits.
779 *
780 * @param deviceId Device ID
781 * @param vlanId vlan ID
782 * @param portNumber Port number
783 * @return L3 unicast group key
784 */
785 public static int doubleVlanL3UnicastGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
786 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
787 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
788 int hash = Objects.hash(deviceId, portHigherBits, vlanId);
789 return L3_UNICAST_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
790 }
791
792 public static int doubleVlanL3UnicastGroupId(VlanId vlanId, long portNum) {
793 // <4bits-2><1bit-1><12bits-vlanId><15bits-portId>
794 return L3_UNICAST_TYPE | 1 << 27 | (vlanId.toShort() << 15) | (int) (portNum & 0x7FFF);
795 }
796
pier9469f3e2019-04-17 17:05:08 +0200797 /**
798 * Helper method to decide whether L2 Interface group or L2 Unfiltered group needs to be created.
799 * L2 Unfiltered group will be created if meta has VlanIdCriterion with VlanId.ANY, and
800 * treatment has set Vlan ID action.
801 *
802 * @param treatment treatment passed in by the application as part of the nextObjective
803 * @param meta metadata passed in by the application as part of the nextObjective
804 * @return true if L2 Unfiltered group needs to be created, false otherwise.
805 */
806 static boolean isUnfiltered(TrafficTreatment treatment, TrafficSelector meta) {
807 if (meta == null || treatment == null) {
808 return false;
809 }
810 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) meta.getCriterion(Criterion.Type.VLAN_VID);
811 if (vlanIdCriterion == null || !vlanIdCriterion.vlanId().equals(VlanId.ANY)) {
812 return false;
813 }
814
815 return treatment.allInstructions().stream()
816 .filter(i -> (i.type() == Instruction.Type.L2MODIFICATION
817 && ((L2ModificationInstruction) i).subtype() == L2ModificationInstruction.L2SubType.VLAN_ID))
818 .count() == 1;
819 }
820
Yi Tsengef19de12017-04-24 11:33:05 -0700821}