blob: beabae0d34a8948d98681998f3d7d0b4a58bfe84 [file] [log] [blame]
Charles Chan425854b2016-04-11 15:32:12 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Charles Chan425854b2016-04-11 15:32:12 -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
Yi Tsengef19de12017-04-24 11:33:05 -070017package org.onosproject.driver.pipeline.ofdpa;
Charles Chan425854b2016-04-11 15:32:12 -070018
19import org.onlab.packet.MacAddress;
20import org.onlab.packet.VlanId;
21import org.onosproject.core.ApplicationId;
Yi Tsengfa394de2017-02-01 11:26:40 -080022import org.onosproject.core.GroupId;
Charles Chan425854b2016-04-11 15:32:12 -070023import org.onosproject.net.flow.DefaultTrafficTreatment;
24import org.onosproject.net.flow.TrafficSelector;
25import org.onosproject.net.flow.TrafficTreatment;
26import org.onosproject.net.flow.criteria.Criterion;
27import org.onosproject.net.flow.criteria.VlanIdCriterion;
28import org.onosproject.net.flow.instructions.Instruction;
29import org.onosproject.net.flow.instructions.Instructions;
30import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Pier Ventre140a8942016-11-02 07:26:38 -070031import org.onosproject.net.flowobjective.NextObjective;
Charles Chan425854b2016-04-11 15:32:12 -070032import org.onosproject.net.group.DefaultGroupBucket;
33import org.onosproject.net.group.DefaultGroupDescription;
34import org.onosproject.net.group.DefaultGroupKey;
35import org.onosproject.net.group.GroupBucket;
36import org.onosproject.net.group.GroupBuckets;
37import org.onosproject.net.group.GroupDescription;
38import org.onosproject.net.group.GroupKey;
39import org.slf4j.Logger;
40
Pier Ventre140a8942016-11-02 07:26:38 -070041import java.util.ArrayList;
Charles Chan425854b2016-04-11 15:32:12 -070042import java.util.Collections;
Pier Ventre140a8942016-11-02 07:26:38 -070043import java.util.Deque;
44import java.util.List;
Charles Chan425854b2016-04-11 15:32:12 -070045
Yi Tsengef19de12017-04-24 11:33:05 -070046import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*;
Charles Chan425854b2016-04-11 15:32:12 -070047import static org.slf4j.LoggerFactory.getLogger;
48
49/**
Charles Chanab591602019-01-22 17:25:04 -080050 * Group handler for Open vSwitch OFDPA pipeline.
Charles Chan425854b2016-04-11 15:32:12 -070051 */
Charles Chanab591602019-01-22 17:25:04 -080052public class OvsOfdpaGroupHandler extends Ofdpa2GroupHandler {
Charles Chan425854b2016-04-11 15:32:12 -070053 private final Logger log = getLogger(getClass());
54
55 @Override
Charles Chanab591602019-01-22 17:25:04 -080056 protected boolean supportCopyTtl() {
57 return false;
58 }
59
60 @Override
61 protected boolean supportSetMplsBos() {
62 return false;
63 }
64
65 @Override
66 protected boolean requireVlanPopBeforeMplsPush() {
67 return true;
68 }
69
70 @Override
Charles Chan367c1c12018-10-19 16:23:28 -070071 protected boolean requireAllowVlanTransition() {
72 return false;
73 }
74
75 @Override
Charles Chan425854b2016-04-11 15:32:12 -070076 protected GroupInfo createL2L3Chain(TrafficTreatment treatment, int nextId,
Yi Tsengef19de12017-04-24 11:33:05 -070077 ApplicationId appId, boolean mpls,
78 TrafficSelector meta) {
Andrea Campanella7c977b92018-05-30 21:39:45 -070079 if (createUnfiltered(treatment, meta)) {
80 return createUnfilteredL2L3Chain(treatment, nextId, appId);
81 }
Charles Chan425854b2016-04-11 15:32:12 -070082 // for the l2interface group, get vlan and port info
83 // for the outer group, get the src/dst mac, and vlan info
84 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
85 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
86 VlanId vlanid = null;
87 long portNum = 0;
88 boolean setVlan = false, popVlan = false;
89 MacAddress srcMac = MacAddress.ZERO;
90 MacAddress dstMac = MacAddress.ZERO;
91 for (Instruction ins : treatment.allInstructions()) {
92 if (ins.type() == Instruction.Type.L2MODIFICATION) {
93 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
94 switch (l2ins.subtype()) {
95 case ETH_DST:
96 dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
97 outerTtb.setEthDst(dstMac);
98 break;
99 case ETH_SRC:
100 srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
101 outerTtb.setEthSrc(srcMac);
102 break;
103 case VLAN_ID:
104 vlanid = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
105 outerTtb.setVlanId(vlanid);
106 setVlan = true;
107 break;
108 case VLAN_POP:
109 innerTtb.popVlan();
110 popVlan = true;
111 break;
112 case DEC_MPLS_TTL:
113 case MPLS_LABEL:
114 case MPLS_POP:
115 case MPLS_PUSH:
116 case VLAN_PCP:
117 case VLAN_PUSH:
118 default:
119 break;
120 }
121 } else if (ins.type() == Instruction.Type.OUTPUT) {
122 portNum = ((Instructions.OutputInstruction) ins).port().toLong();
123 innerTtb.add(ins);
124 } else {
Saurav Dasa4020382018-02-14 14:14:54 -0800125 log.debug("Driver does not handle this type of TrafficTreatment"
126 + " instruction in l2l3chain: {} - {}", ins.type(),
127 ins);
Charles Chan425854b2016-04-11 15:32:12 -0700128 }
129 }
130
131 if (vlanid == null && meta != null) {
132 // use metadata if available
133 Criterion vidCriterion = meta.getCriterion(Criterion.Type.VLAN_VID);
134 if (vidCriterion != null) {
135 vlanid = ((VlanIdCriterion) vidCriterion).vlanId();
136 }
137 // if vlan is not set, use the vlan in metadata for outerTtb
138 if (vlanid != null && !setVlan) {
139 outerTtb.setVlanId(vlanid);
140 }
141 }
142
143 if (vlanid == null) {
144 log.error("Driver cannot process an L2/L3 group chain without "
Pier Ventre140a8942016-11-02 07:26:38 -0700145 + "egress vlan information for dev: {} port:{}",
146 deviceId, portNum);
Charles Chan425854b2016-04-11 15:32:12 -0700147 return null;
148 }
149
150 if (!setVlan && !popVlan) {
151 // untagged outgoing port
152 TrafficTreatment.Builder temp = DefaultTrafficTreatment.builder();
153 temp.popVlan();
154 innerTtb.build().allInstructions().forEach(i -> temp.add(i));
155 innerTtb = temp;
156 }
157
158 // assemble information for ofdpa l2interface group
159 int l2groupId = L2_INTERFACE_TYPE | (vlanid.toShort() << 16) | (int) portNum;
160 // a globally unique groupkey that is different for ports in the same device,
161 // but different for the same portnumber on different devices. Also different
162 // for the various group-types created out of the same next objective.
163 int l2gk = l2InterfaceGroupKey(deviceId, vlanid, portNum);
164 final GroupKey l2groupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
165
166 // assemble information for outer group
167 GroupDescription outerGrpDesc = null;
168 if (mpls) {
169 // outer group is MPLSInteface
170 int mplsInterfaceIndex = getNextAvailableIndex();
171 int mplsgroupId = MPLS_INTERFACE_TYPE | (SUBTYPE_MASK & mplsInterfaceIndex);
172 final GroupKey mplsgroupkey = new DefaultGroupKey(
Pier Ventre140a8942016-11-02 07:26:38 -0700173 Ofdpa2Pipeline.appKryo.serialize(mplsInterfaceIndex));
Yi Tsengfa394de2017-02-01 11:26:40 -0800174 outerTtb.group(new GroupId(l2groupId));
Charles Chan425854b2016-04-11 15:32:12 -0700175 // create the mpls-interface group description to wait for the
176 // l2 interface group to be processed
177 GroupBucket mplsinterfaceGroupBucket =
178 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
179 outerGrpDesc = new DefaultGroupDescription(
180 deviceId,
181 GroupDescription.Type.INDIRECT,
182 new GroupBuckets(Collections.singletonList(
183 mplsinterfaceGroupBucket)),
184 mplsgroupkey,
185 mplsgroupId,
186 appId);
187 log.debug("Trying MPLS-Interface: device:{} gid:{} gkey:{} nextid:{}",
Pier Ventre140a8942016-11-02 07:26:38 -0700188 deviceId, Integer.toHexString(mplsgroupId),
189 mplsgroupkey, nextId);
Charles Chan425854b2016-04-11 15:32:12 -0700190 } else {
191 // outer group is L3Unicast
192 int l3unicastIndex = getNextAvailableIndex();
193 int l3groupId = L3_UNICAST_TYPE | (TYPE_MASK & l3unicastIndex);
194 final GroupKey l3groupkey = new DefaultGroupKey(
Pier Ventre140a8942016-11-02 07:26:38 -0700195 Ofdpa2Pipeline.appKryo.serialize(l3unicastIndex));
Yi Tsengfa394de2017-02-01 11:26:40 -0800196 outerTtb.group(new GroupId(l2groupId));
Charles Chan425854b2016-04-11 15:32:12 -0700197 // create the l3unicast group description to wait for the
198 // l2 interface group to be processed
199 GroupBucket l3unicastGroupBucket =
200 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
201 outerGrpDesc = new DefaultGroupDescription(
202 deviceId,
203 GroupDescription.Type.INDIRECT,
204 new GroupBuckets(Collections.singletonList(
205 l3unicastGroupBucket)),
206 l3groupkey,
207 l3groupId,
208 appId);
209 log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
Pier Ventre140a8942016-11-02 07:26:38 -0700210 deviceId, Integer.toHexString(l3groupId),
211 l3groupkey, nextId);
Charles Chan425854b2016-04-11 15:32:12 -0700212 }
213
214 // store l2groupkey with the groupChainElem for the outer-group that depends on it
Yi Tsengef19de12017-04-24 11:33:05 -0700215 GroupChainElem gce = new GroupChainElem(outerGrpDesc,
216 1,
217 false,
218 deviceId);
Charles Chan425854b2016-04-11 15:32:12 -0700219 updatePendingGroups(l2groupkey, gce);
220
221 // create group description for the inner l2interfacegroup
222 GroupBucket l2InterfaceGroupBucket =
223 DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
224 GroupDescription l2groupDescription =
225 new DefaultGroupDescription(
226 deviceId,
227 GroupDescription.Type.INDIRECT,
228 new GroupBuckets(Collections.singletonList(
229 l2InterfaceGroupBucket)),
230 l2groupkey,
231 l2groupId,
232 appId);
233 log.debug("Trying L2Interface: device:{} gid:{} gkey:{} nextId:{}",
Pier Ventre140a8942016-11-02 07:26:38 -0700234 deviceId, Integer.toHexString(l2groupId),
235 l2groupkey, nextId);
Charles Chan425854b2016-04-11 15:32:12 -0700236 return new GroupInfo(l2groupDescription, outerGrpDesc);
237 }
Pier Ventre140a8942016-11-02 07:26:38 -0700238
Pier Ventre140a8942016-11-02 07:26:38 -0700239 /**
240 * In OFDPA2 we do not support the MPLS-ECMP, while we do in
Charles Chanab591602019-01-22 17:25:04 -0800241 * Open vSwitch implementation.
Pier Ventre140a8942016-11-02 07:26:38 -0700242 *
243 * @param nextObjective the hashed next objective to support.
244 */
245 @Override
Charles Chan367c1c12018-10-19 16:23:28 -0700246 protected void processEcmpHashedNextObjective(NextObjective nextObjective) {
Pier Ventre140a8942016-11-02 07:26:38 -0700247 // The case for MPLS-ECMP. For now, we try to create a MPLS-ECMP for
248 // the transport of a VPWS. The necessary info are contained in the
249 // meta selector. In particular we are looking for the case of BoS==False;
250 TrafficSelector metaSelector = nextObjective.meta();
Yi Tsengef19de12017-04-24 11:33:05 -0700251 if (metaSelector != null && Ofdpa2Pipeline.isNotMplsBos(metaSelector)) {
Pier Ventre140a8942016-11-02 07:26:38 -0700252 // storage for all group keys in the chain of groups created
253 List<Deque<GroupKey>> allGroupKeys = new ArrayList<>();
254 List<GroupInfo> unsentGroups = new ArrayList<>();
Charles Chan367c1c12018-10-19 16:23:28 -0700255 createEcmpHashBucketChains(nextObjective, allGroupKeys, unsentGroups);
Pier Ventre140a8942016-11-02 07:26:38 -0700256 // now we can create the outermost MPLS ECMP group
257 List<GroupBucket> mplsEcmpGroupBuckets = new ArrayList<>();
258 for (GroupInfo gi : unsentGroups) {
259 // create ECMP bucket to point to the outer group
260 TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder();
Yi Tsengef19de12017-04-24 11:33:05 -0700261 ttb.group(new GroupId(gi.nextGroupDesc().givenGroupId()));
Pier Ventre140a8942016-11-02 07:26:38 -0700262 GroupBucket sbucket = DefaultGroupBucket
263 .createSelectGroupBucket(ttb.build());
264 mplsEcmpGroupBuckets.add(sbucket);
265 }
266 int mplsEcmpIndex = getNextAvailableIndex();
Yi Tsengef19de12017-04-24 11:33:05 -0700267 int mplsEcmpGroupId = makeMplsForwardingGroupId(OfdpaMplsGroupSubType.MPLS_ECMP, mplsEcmpIndex);
Pier Ventre140a8942016-11-02 07:26:38 -0700268 GroupKey mplsEmpGroupKey = new DefaultGroupKey(
269 Ofdpa2Pipeline.appKryo.serialize(mplsEcmpIndex)
270 );
271 GroupDescription mplsEcmpGroupDesc = new DefaultGroupDescription(
272 deviceId,
273 GroupDescription.Type.SELECT,
274 new GroupBuckets(mplsEcmpGroupBuckets),
275 mplsEmpGroupKey,
276 mplsEcmpGroupId,
277 nextObjective.appId()
278 );
279 GroupChainElem mplsEcmpGce = new GroupChainElem(mplsEcmpGroupDesc,
280 mplsEcmpGroupBuckets.size(),
Yi Tsengef19de12017-04-24 11:33:05 -0700281 false,
282 deviceId);
Pier Ventre140a8942016-11-02 07:26:38 -0700283
284 // create objects for local and distributed storage
285 allGroupKeys.forEach(gkeyChain -> gkeyChain.addFirst(mplsEmpGroupKey));
286 OfdpaNextGroup ofdpaGrp = new OfdpaNextGroup(allGroupKeys, nextObjective);
287
288 // store mplsEcmpGroupKey with the ofdpaGroupChain for the nextObjective
289 // that depends on it
290 updatePendingNextObjective(mplsEmpGroupKey, ofdpaGrp);
291
292 log.debug("Trying MPLS-ECMP: device:{} gid:{} gkey:{} nextId:{}",
293 deviceId, Integer.toHexString(mplsEcmpGroupId),
294 mplsEmpGroupKey, nextObjective.id());
295
296 // finally we are ready to send the innermost groups
297 for (GroupInfo gi : unsentGroups) {
298 log.debug("Sending innermost group {} in group chain on device {} ",
Yi Tsengef19de12017-04-24 11:33:05 -0700299 Integer.toHexString(gi.innerMostGroupDesc().givenGroupId()), deviceId);
300 updatePendingGroups(gi.nextGroupDesc().appCookie(), mplsEcmpGce);
301 groupService.addGroup(gi.innerMostGroupDesc());
Pier Ventre140a8942016-11-02 07:26:38 -0700302 }
303 return;
304 }
Charles Chan367c1c12018-10-19 16:23:28 -0700305 super.processEcmpHashedNextObjective(nextObjective);
Pier Ventre140a8942016-11-02 07:26:38 -0700306 }
Andrea Campanella7c977b92018-05-30 21:39:45 -0700307
308 /**
309 * Internal implementation of createL2L3Chain to handle double-tagged vlan.
310 * L3UG Group carries dummyVlanId and output port information in its groupId,
311 * and does not set vlan.
312 * L2UG Group only has OUTPUT instruction.
313 *
314 * @param treatment that needs to be broken up to create the group chain
315 * @param nextId of the next objective that needs this group chain
316 * @param appId of the application that sent this next objective
317 * @return GroupInfo containing the GroupDescription of the
318 * L2 Unfiltered Interface group(inner) and the GroupDescription of the (outer)
319 * L3Unicast group. May return null if there is an error in processing the chain.
320 */
321 private GroupInfo createUnfilteredL2L3Chain(TrafficTreatment treatment, int nextId,
322 ApplicationId appId) {
323 // for the l2 unfiltered interface group, get port info
324 // for the l3 unicast group, get the src/dst mac, and vlan info
325 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
326 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
327 VlanId vlanId = VlanId.NONE;
328 long portNum = 0;
329 MacAddress srcMac;
330 MacAddress dstMac;
331 for (Instruction ins : treatment.allInstructions()) {
332 if (ins.type() == Instruction.Type.L2MODIFICATION) {
333 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
334 switch (l2ins.subtype()) {
335 case ETH_DST:
336 dstMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
337 outerTtb.setEthDst(dstMac);
338 break;
339 case ETH_SRC:
340 srcMac = ((L2ModificationInstruction.ModEtherInstruction) l2ins).mac();
341 outerTtb.setEthSrc(srcMac);
342 break;
343 case VLAN_ID:
344 vlanId = ((L2ModificationInstruction.ModVlanIdInstruction) l2ins).vlanId();
345 break;
346 default:
347 break;
348 }
349 } else if (ins.type() == Instruction.Type.OUTPUT) {
350 portNum = ((Instructions.OutputInstruction) ins).port().toLong();
351 innerTtb.add(ins);
352 } else {
353 log.debug("Driver does not handle this type of TrafficTreatment"
354 + " instruction in l2l3chain: {} - {}", ins.type(),
355 ins);
356 }
357 }
358
359 // assemble information for ofdpa l2 unfiltered interface group
360 int l2groupId = l2UnfilteredGroupId(portNum);
361 // a globally unique groupkey that is different for ports in the same device,
362 // but different for the same portnumber on different devices. Also different
363 // for the various group-types created out of the same next objective.
364 int l2gk = l2UnfilteredGroupKey(deviceId, portNum);
365 final GroupKey l2groupkey = new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(l2gk));
366
367 // assemble information for outer group (L3Unicast)
368 GroupDescription outerGrpDesc;
369 int l3groupId = doubleVlanL3UnicastGroupId(vlanId, portNum);
370 final GroupKey l3groupkey = new DefaultGroupKey(
371 Ofdpa3Pipeline.appKryo.serialize(doubleVlanL3UnicastGroupKey(deviceId, vlanId, portNum)));
372 outerTtb.group(new GroupId(l2groupId));
373 // create the l3unicast group description to wait for the
374 // l2 unfiltered interface group to be processed
375 GroupBucket l3unicastGroupBucket =
376 DefaultGroupBucket.createIndirectGroupBucket(outerTtb.build());
377 outerGrpDesc = new DefaultGroupDescription(
378 deviceId,
379 GroupDescription.Type.INDIRECT,
380 new GroupBuckets(Collections.singletonList(l3unicastGroupBucket)),
381 l3groupkey,
382 l3groupId,
383 appId);
384 log.debug("Trying L3Unicast: device:{} gid:{} gkey:{} nextid:{}",
385 deviceId, Integer.toHexString(l3groupId),
386 l3groupkey, nextId);
387
388 // store l2groupkey with the groupChainElem for the outer-group that depends on it
389 OfdpaGroupHandlerUtility.GroupChainElem gce = new OfdpaGroupHandlerUtility.GroupChainElem(
390 outerGrpDesc, 1, false, deviceId);
391 updatePendingGroups(l2groupkey, gce);
392
393 // create group description for the inner l2 unfiltered interface group
394 GroupBucket l2InterfaceGroupBucket =
395 DefaultGroupBucket.createIndirectGroupBucket(innerTtb.build());
396 GroupDescription l2groupDescription =
397 new DefaultGroupDescription(deviceId,
398 GroupDescription.Type.INDIRECT,
399 new GroupBuckets(Collections.singletonList(l2InterfaceGroupBucket)),
400 l2groupkey,
401 l2groupId,
402 appId);
403 log.debug("Trying L2Unfiltered: device:{} gid:{} gkey:{} nextId:{}",
404 deviceId, Integer.toHexString(l2groupId), l2groupkey, nextId);
405 return new OfdpaGroupHandlerUtility.GroupInfo(l2groupDescription, outerGrpDesc);
406 }
407
Charles Chan425854b2016-04-11 15:32:12 -0700408}