blob: a05892394b48ec6adb3e5057d2e825efda8fb0b0 [file] [log] [blame]
Yi Tsengef19de12017-04-24 11:33:05 -07001/*
2 * Copyright 2017-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
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;
30import org.onosproject.net.flow.instructions.Instruction;
31import org.onosproject.net.flow.instructions.Instructions;
32import org.onosproject.net.flowobjective.NextObjective;
33import org.onosproject.net.group.DefaultGroupBucket;
34import org.onosproject.net.group.DefaultGroupKey;
35import org.onosproject.net.group.Group;
36import org.onosproject.net.group.GroupBucket;
37import org.onosproject.net.group.GroupDescription;
38import org.onosproject.net.group.GroupKey;
39import org.onosproject.net.group.GroupService;
40import org.slf4j.Logger;
41
42import java.util.Deque;
43import java.util.List;
44import java.util.Objects;
45import java.util.Set;
46import java.util.concurrent.atomic.AtomicInteger;
47import java.util.stream.Collectors;
48
49import static org.onosproject.driver.pipeline.ofdpa.Ofdpa2Pipeline.isNotMplsBos;
50import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.OfdpaMplsGroupSubType.OFDPA_GROUP_TYPE_SHIFT;
51import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.OfdpaMplsGroupSubType.OFDPA_MPLS_SUBTYPE_SHIFT;
52import static org.onosproject.net.flowobjective.NextObjective.Type.HASHED;
53import static org.slf4j.LoggerFactory.getLogger;
54
55public final class OfdpaGroupHandlerUtility {
56 /*
57 * OFDPA requires group-id's to have a certain form.
58 * L2 Interface Groups have <4bits-0><12bits-vlanId><16bits-portId>
59 * L3 Unicast Groups have <4bits-2><28bits-index>
60 * MPLS Interface Groups have <4bits-9><4bits:0><24bits-index>
61 * L3 ECMP Groups have <4bits-7><28bits-index>
62 * L2 Flood Groups have <4bits-4><12bits-vlanId><16bits-index>
63 * L3 VPN Groups have <4bits-9><4bits-2><24bits-index>
64 */
65 protected static final int L2_INTERFACE_TYPE = 0x00000000;
66 protected static final int L3_INTERFACE_TYPE = 0x50000000;
67 protected static final int L3_UNICAST_TYPE = 0x20000000;
68 protected static final int L3_MULTICAST_TYPE = 0x60000000;
69 protected static final int MPLS_INTERFACE_TYPE = 0x90000000;
70 protected static final int MPLS_L3VPN_SUBTYPE = 0x92000000;
71 protected static final int L3_ECMP_TYPE = 0x70000000;
72 protected static final int L2_FLOOD_TYPE = 0x40000000;
73
74 protected static final int TYPE_MASK = 0x0fffffff;
75 protected static final int SUBTYPE_MASK = 0x00ffffff;
76 protected static final int TYPE_VLAN_MASK = 0x0000ffff;
77
78 protected static final int THREE_BIT_MASK = 0x0fff;
79 protected static final int FOUR_BIT_MASK = 0xffff;
80 protected static final int PORT_LEN = 16;
81
82 protected static final int PORT_LOWER_BITS_MASK = 0x3f;
83 protected static final long PORT_HIGHER_BITS_MASK = ~PORT_LOWER_BITS_MASK;
84
85 protected static final String HEX_PREFIX = "0x";
86 protected static final Logger log = getLogger(OfdpaGroupHandlerUtility.class);
87
88 private OfdpaGroupHandlerUtility() {
89 // Utility classes should not have a public or default constructor.
90 }
91
92 /**
93 * Returns the outport in a traffic treatment.
94 *
95 * @param tt the treatment
96 * @return the PortNumber for the outport or null
97 */
98 protected static PortNumber readOutPortFromTreatment(TrafficTreatment tt) {
99 for (Instruction ins : tt.allInstructions()) {
100 if (ins.type() == Instruction.Type.OUTPUT) {
101 return ((Instructions.OutputInstruction) ins).port();
102 }
103 }
104 return null;
105 }
106
107 /**
108 * Helper enum to handle the different MPLS group
109 * types.
110 */
111 public enum OfdpaMplsGroupSubType {
112 MPLS_INTF((short) 0),
113 L2_VPN((short) 1),
114 L3_VPN((short) 2),
115 MPLS_TUNNEL_LABEL_1((short) 3),
116 MPLS_TUNNEL_LABEL_2((short) 4),
117 MPLS_SWAP_LABEL((short) 5),
118 MPLS_ECMP((short) 8);
119
120 private short value;
121 public static final int OFDPA_GROUP_TYPE_SHIFT = 28;
122 public static final int OFDPA_MPLS_SUBTYPE_SHIFT = 24;
123
124 OfdpaMplsGroupSubType(short value) {
125 this.value = value;
126 }
127
128 /**
129 * Gets the value as an short.
130 *
131 * @return the value as an short
132 */
133 public short getValue() {
134 return this.value;
135 }
136
137 }
138
139 /**
140 * Creates MPLS Label group id given a sub type and
141 * the index.
142 *
143 * @param subType the MPLS Label group sub type
144 * @param index the index of the group
145 * @return the OFDPA group id
146 */
147 public static Integer makeMplsLabelGroupId(OfdpaMplsGroupSubType subType, int index) {
148 index = index & 0x00FFFFFF;
149 return index | (9 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
150 }
151
152 /**
153 * Creates MPLS Forwarding group id given a sub type and
154 * the index.
155 *
156 * @param subType the MPLS forwarding group sub type
157 * @param index the index of the group
158 * @return the OFDPA group id
159 */
160 public static Integer makeMplsForwardingGroupId(OfdpaMplsGroupSubType subType, int index) {
161 index = index & 0x00FFFFFF;
162 return index | (10 << OFDPA_GROUP_TYPE_SHIFT) | (subType.value << OFDPA_MPLS_SUBTYPE_SHIFT);
163 }
164
165 /**
166 * Gets duplicated output ports between group key chains and existing groups
167 * in the device.
168 *
169 * @param allActiveKeys list of group key chain
170 * @param groupService the group service to get group information
171 * @param deviceId the device id to get group
172 * @return a set of output port from the list of group key chain
173 */
174 public static Set<PortNumber> getExistingOutputPorts(List<Deque<GroupKey>> allActiveKeys,
175 GroupService groupService,
176 DeviceId deviceId) {
177 Set<PortNumber> existingPorts = Sets.newHashSet();
178
179 allActiveKeys.forEach(keyChain -> {
180 GroupKey ifaceGroupKey = keyChain.peekLast();
181 Group ifaceGroup = groupService.getGroup(deviceId, ifaceGroupKey);
182 if (ifaceGroup != null && !ifaceGroup.buckets().buckets().isEmpty()) {
183 ifaceGroup.buckets().buckets().forEach(bucket -> {
184 PortNumber portNumber = readOutPortFromTreatment(bucket.treatment());
185 if (portNumber != null) {
186 existingPorts.add(portNumber);
187 }
188 });
189 }
190 });
191 return existingPorts;
192 }
193
194 /**
195 * The purpose of this function is to verify if the hashed next
196 * objective is supported by the current pipeline.
197 *
198 * @param nextObjective the hashed objective to verify
199 * @return true if the hashed objective is supported. Otherwise false.
200 */
201 public static boolean verifyHashedNextObjective(NextObjective nextObjective) {
202 // if it is not hashed, there is something wrong;
203 if (nextObjective.type() != HASHED) {
204 return false;
205 }
206 // The case non supported is the MPLS-ECMP. For now, we try
207 // to create a MPLS-ECMP for the transport of a VPWS. The
208 // necessary info are contained in the meta selector. In particular
209 // we are looking for the case of BoS==False;
210 TrafficSelector metaSelector = nextObjective.meta();
211 if (metaSelector != null && isNotMplsBos(metaSelector)) {
212 return false;
213 }
214
215 return true;
216 }
217
218 /**
219 * Generates a list of group buckets from given list of group information
220 * and group bucket type.
221 *
222 * @param groupInfos a list of group information
223 * @param bucketType group bucket type
224 * @return list of group bucket generate from group information
225 */
226 protected static List<GroupBucket> generateNextGroupBuckets(List<GroupInfo> groupInfos,
227 GroupDescription.Type bucketType) {
228 List<GroupBucket> newBuckets = Lists.newArrayList();
229
230 groupInfos.forEach(groupInfo -> {
231 GroupDescription groupDesc = groupInfo.nextGroupDesc();
232 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
233 treatmentBuilder.group(new GroupId(groupDesc.givenGroupId()));
234 GroupBucket newBucket = null;
235 switch (bucketType) {
236 case ALL:
237 newBucket =
238 DefaultGroupBucket.createAllGroupBucket(treatmentBuilder.build());
239 break;
240 case INDIRECT:
241 newBucket =
242 DefaultGroupBucket.createIndirectGroupBucket(treatmentBuilder.build());
243 break;
244 case SELECT:
245 newBucket =
246 DefaultGroupBucket.createSelectGroupBucket(treatmentBuilder.build());
247 break;
248 case FAILOVER:
249 // TODO: support failover bucket type
250 default:
251 log.warn("Unknown bucket type: {}", bucketType);
252 break;
253 }
254
255 if (newBucket != null) {
256 newBuckets.add(newBucket);
257 }
258
259 });
260
261 return ImmutableList.copyOf(newBuckets);
262 }
263
264 /**
265 * Extracts VlanId from given group ID.
266 *
267 * @param groupId the group ID
268 * @return vlan id of the group
269 */
270 public static VlanId extractVlanIdFromGroupId(int groupId) {
271 // Extract the 9th to 20th bit from group id as vlan id.
272 short vlanId = (short) ((groupId & 0x0fff0000) >> 16);
273 return VlanId.vlanId(vlanId);
274 }
275
276 public static GroupKey l2FloodGroupKey(VlanId vlanId, DeviceId deviceId) {
277 int hash = Objects.hash(deviceId, vlanId);
278 hash = L2_FLOOD_TYPE | TYPE_MASK & hash;
279 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
280 }
281
282 public static int l2GroupId(VlanId vlanId, long portNum) {
283 return L2_INTERFACE_TYPE | (vlanId.toShort() << 16) | (int) portNum;
284 }
285
286 /**
287 * Returns a hash as the L2 Interface Group Key.
288 *
289 * Keep the lower 6-bit for port since port number usually smaller than 64.
290 * Hash other information into remaining 28 bits.
291 *
292 * @param deviceId Device ID
293 * @param vlanId VLAN ID
294 * @param portNumber Port number
295 * @return L2 interface group key
296 */
297 public static int l2InterfaceGroupKey(DeviceId deviceId, VlanId vlanId, long portNumber) {
298 int portLowerBits = (int) portNumber & PORT_LOWER_BITS_MASK;
299 long portHigherBits = portNumber & PORT_HIGHER_BITS_MASK;
300 int hash = Objects.hash(deviceId, vlanId, portHigherBits);
301 return L2_INTERFACE_TYPE | (TYPE_MASK & hash << 6) | portLowerBits;
302 }
303
304 /**
305 * Utility class for moving group information around.
306 *
307 * Example: Suppose we are trying to create a group-chain A-B-C-D, where
308 * A is the top level group, and D is the inner-most group, typically L2 Interface.
309 * The innerMostGroupDesc is always D. At various stages of the creation
310 * process the nextGroupDesc may be C or B. The nextGroupDesc exists to
311 * inform the referencing group about which group it needs to point to,
312 * and wait for. In some cases the group chain may simply be A-B. In this case,
313 * both innerMostGroupDesc and nextGroupDesc will be B.
314 */
315 public static class GroupInfo {
316 /**
317 * Description of the inner-most group of the group chain.
318 * It is always an L2 interface group.
319 */
320 private GroupDescription innerMostGroupDesc;
321
322 /**
323 * Description of the next group in the group chain.
324 * It can be L2 interface, L3 interface, L3 unicast, L3 VPN group.
325 * It is possible that nextGroupDesc is the same as the innerMostGroup.
326 */
327 private GroupDescription nextGroupDesc;
328
329 GroupInfo(GroupDescription innerMostGroupDesc, GroupDescription nextGroupDesc) {
330 this.innerMostGroupDesc = innerMostGroupDesc;
331 this.nextGroupDesc = nextGroupDesc;
332 }
333
334 /**
335 * Getter for innerMostGroupDesc.
336 *
337 * @return the inner most group description
338 */
339 public GroupDescription innerMostGroupDesc() {
340 return innerMostGroupDesc;
341 }
342
343 /**
344 * Getter for the next group description.
345 *
346 * @return the next group description
347 */
348 public GroupDescription nextGroupDesc() {
349 return nextGroupDesc;
350 }
351
352 /**
353 * Setter of nextGroupDesc.
354 *
355 * @param nextGroupDesc the given value to set
356 */
357 public void nextGroupDesc(GroupDescription nextGroupDesc) {
358 this.nextGroupDesc = nextGroupDesc;
359 }
360 }
361
362 /**
363 * Represents an entire group-chain that implements a Next-Objective from
364 * the application. The objective is represented as a list of deques, where
365 * each deque is a separate chain of groups.
366 * <p>
367 * For example, an ECMP group with 3 buckets, where each bucket points to
368 * a group chain of L3 Unicast and L2 interface groups will look like this:
369 * <ul>
370 * <li>List[0] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
371 * <li>List[1] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
372 * <li>List[2] is a Deque of GroupKeyECMP(first)-GroupKeyL3(middle)-GroupKeyL2(last)
373 * </ul>
374 * where the first element of each deque is the same, representing the
375 * top level ECMP group, while every other element represents a unique groupKey.
376 * <p>
377 * Also includes information about the next objective that
378 * resulted in these group-chains.
379 *
380 */
381 public static class OfdpaNextGroup implements NextGroup {
382 private final NextObjective nextObj;
383 private final List<Deque<GroupKey>> gkeys;
384
385 public OfdpaNextGroup(List<Deque<GroupKey>> gkeys, NextObjective nextObj) {
386 this.nextObj = nextObj;
387 this.gkeys = gkeys;
388 }
389
390 public NextObjective nextObjective() {
391 return nextObj;
392 }
393
394 @Override
395 public byte[] data() {
396 return Ofdpa2Pipeline.appKryo.serialize(gkeys);
397 }
398 }
399
400 /**
401 * Represents a group element that is part of a chain of groups.
402 * Stores enough information to create a Group Description to add the group
403 * to the switch by requesting the Group Service. Objects instantiating this
404 * class are meant to be temporary and live as long as it is needed to wait for
405 * referenced groups in the group chain to be created.
406 */
407 public static class GroupChainElem {
408 private GroupDescription groupDescription;
409 private AtomicInteger waitOnGroups;
410 private boolean addBucketToGroup;
411 private DeviceId deviceId;
412
413 public GroupChainElem(GroupDescription groupDescription, int waitOnGroups,
414 boolean addBucketToGroup, DeviceId deviceId) {
415 this.groupDescription = groupDescription;
416 this.waitOnGroups = new AtomicInteger(waitOnGroups);
417 this.addBucketToGroup = addBucketToGroup;
418 this.deviceId = deviceId;
419 }
420
421 /**
422 * This method atomically decrements the counter for the number of
423 * groups this GroupChainElement is waiting on, for notifications from
424 * the Group Service. When this method returns a value of 0, this
425 * GroupChainElement is ready to be processed.
426 *
427 * @return integer indication of the number of notifications being waited on
428 */
429 int decrementAndGetGroupsWaitedOn() {
430 return waitOnGroups.decrementAndGet();
431 }
432
433 public GroupDescription groupDescription() {
434 return groupDescription;
435 }
436
437 public boolean addBucketToGroup() {
438 return addBucketToGroup;
439 }
440
441 @Override
442 public String toString() {
443 return (Integer.toHexString(groupDescription.givenGroupId()) +
444 " groupKey: " + groupDescription.appCookie() +
445 " waiting-on-groups: " + waitOnGroups.get() +
446 " addBucketToGroup: " + addBucketToGroup +
447 " device: " + deviceId);
448 }
449 }
450
451 public static class GroupChecker implements Runnable {
452 protected final Logger log = getLogger(getClass());
453 private Ofdpa2GroupHandler groupHandler;
454
455 public GroupChecker(Ofdpa2GroupHandler groupHandler) {
456 this.groupHandler = groupHandler;
457 }
458
459 @Override
460 public void run() {
461 if (groupHandler.pendingGroups().size() != 0) {
462 log.debug("pending groups being checked: {}", groupHandler.pendingGroups().asMap().keySet());
463 }
464 if (groupHandler.pendingAddNextObjectives().size() != 0) {
465 log.debug("pending add-next-obj being checked: {}",
466 groupHandler.pendingAddNextObjectives().asMap().keySet());
467 }
468 Set<GroupKey> keys = groupHandler.pendingGroups().asMap().keySet().stream()
469 .filter(key -> groupHandler.groupService.getGroup(groupHandler.deviceId, key) != null)
470 .collect(Collectors.toSet());
471 Set<GroupKey> otherkeys = groupHandler.pendingAddNextObjectives().asMap().keySet().stream()
472 .filter(otherkey -> groupHandler.groupService.getGroup(groupHandler.deviceId, otherkey) != null)
473 .collect(Collectors.toSet());
474 keys.addAll(otherkeys);
475
476 keys.forEach(key -> groupHandler.processPendingAddGroupsOrNextObjs(key, false));
477 }
478 }
479}