blob: 7168f6d40549cedb140397d8eba6af121c8fe77c [file] [log] [blame]
Charles Chanf9e98652016-09-07 16:54:23 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Charles Chanf9e98652016-09-07 16:54:23 -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 Chanf9e98652016-09-07 16:54:23 -070018
Pier Ventre42287df2016-11-09 14:17:26 -080019import com.google.common.collect.ImmutableList;
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070020import org.onlab.packet.MacAddress;
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -070021import org.onlab.packet.VlanId;
Charles Chanf9e98652016-09-07 16:54:23 -070022import org.onosproject.core.ApplicationId;
Pier Ventre42287df2016-11-09 14:17:26 -080023import org.onosproject.driver.extensions.Ofdpa3MatchMplsL2Port;
24import org.onosproject.driver.extensions.Ofdpa3MatchOvid;
Pier Ventre70d53ba2016-11-17 22:26:29 -080025import org.onosproject.driver.extensions.Ofdpa3PopCw;
26import org.onosproject.driver.extensions.Ofdpa3PopL2Header;
Pier Ventre42287df2016-11-09 14:17:26 -080027import org.onosproject.driver.extensions.Ofdpa3SetMplsL2Port;
28import org.onosproject.driver.extensions.Ofdpa3SetMplsType;
29import org.onosproject.driver.extensions.Ofdpa3SetOvid;
30import org.onosproject.driver.extensions.Ofdpa3SetQosIndex;
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070031import org.onosproject.driver.extensions.OfdpaMatchVlanVid;
32import org.onosproject.net.PortNumber;
Pier Ventre42287df2016-11-09 14:17:26 -080033import org.onosproject.net.behaviour.NextGroup;
Charles Chanf9e98652016-09-07 16:54:23 -070034import org.onosproject.net.behaviour.PipelinerContext;
Pier Ventre42287df2016-11-09 14:17:26 -080035import org.onosproject.net.flow.DefaultFlowRule;
36import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
Charles Chanf9e98652016-09-07 16:54:23 -070038import org.onosproject.net.flow.FlowRule;
Pier Ventre42287df2016-11-09 14:17:26 -080039import org.onosproject.net.flow.FlowRuleOperations;
40import org.onosproject.net.flow.FlowRuleOperationsContext;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070043import org.onosproject.net.flow.criteria.Criteria;
Pier Ventre42287df2016-11-09 14:17:26 -080044import org.onosproject.net.flow.criteria.Criterion;
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070045import org.onosproject.net.flow.criteria.EthCriterion;
Charles Chanf9e98652016-09-07 16:54:23 -070046import org.onosproject.net.flow.criteria.PortCriterion;
Pier Ventre42287df2016-11-09 14:17:26 -080047import org.onosproject.net.flow.criteria.TunnelIdCriterion;
Charles Chanf9e98652016-09-07 16:54:23 -070048import org.onosproject.net.flow.criteria.VlanIdCriterion;
Pier Ventre70d53ba2016-11-17 22:26:29 -080049import org.onosproject.net.flow.instructions.Instruction;
50import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Pier Ventre42287df2016-11-09 14:17:26 -080051import org.onosproject.net.flow.instructions.L2ModificationInstruction;
52import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
Pier Ventre70d53ba2016-11-17 22:26:29 -080053import org.onosproject.net.flow.instructions.L3ModificationInstruction;
Pier Ventre42287df2016-11-09 14:17:26 -080054import org.onosproject.net.flowobjective.FilteringObjective;
Charles Chanf9e98652016-09-07 16:54:23 -070055import org.onosproject.net.flowobjective.ForwardingObjective;
Pier Ventre42287df2016-11-09 14:17:26 -080056import org.onosproject.net.flowobjective.ObjectiveError;
57import org.onosproject.net.group.Group;
58import org.onosproject.net.group.GroupKey;
59import org.slf4j.Logger;
Charles Chanf9e98652016-09-07 16:54:23 -070060
61import java.util.Collection;
Pier Ventre42287df2016-11-09 14:17:26 -080062import java.util.Collections;
63import java.util.Deque;
Charles Chanf9e98652016-09-07 16:54:23 -070064import java.util.List;
65
Jonghwan Hyun800d9d02018-04-09 09:40:50 -070066import static org.onlab.packet.MacAddress.NONE;
Pier Ventre42287df2016-11-09 14:17:26 -080067import static org.onosproject.driver.extensions.Ofdpa3MplsType.VPWS;
Andrea Campanellad980c6d2018-04-30 11:48:55 +020068import static org.onosproject.net.flow.criteria.Criterion.Type.INNER_VLAN_VID;
69import static org.onosproject.net.flow.criteria.Criterion.Type.IN_PORT;
70import static org.onosproject.net.flow.criteria.Criterion.Type.TUNNEL_ID;
71import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
Pier Ventre42287df2016-11-09 14:17:26 -080072import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
73import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
74import static org.slf4j.LoggerFactory.getLogger;
75
Charles Chanf9e98652016-09-07 16:54:23 -070076/**
77 * Pipeliner for Broadcom OF-DPA 3.0 TTP.
78 */
79public class Ofdpa3Pipeline extends Ofdpa2Pipeline {
Pier Ventre42287df2016-11-09 14:17:26 -080080
81 private final Logger log = getLogger(getClass());
82
Charles Chanf9e98652016-09-07 16:54:23 -070083 @Override
Charles Chan40132b32017-01-22 00:19:37 -080084 protected void initDriverId() {
Charles Chanf9e98652016-09-07 16:54:23 -070085 driverId = coreService.registerApplication(
86 "org.onosproject.driver.Ofdpa3Pipeline");
Charles Chan40132b32017-01-22 00:19:37 -080087 }
Charles Chanf9e98652016-09-07 16:54:23 -070088
Charles Chan40132b32017-01-22 00:19:37 -080089 @Override
90 protected void initGroupHander(PipelinerContext context) {
91 groupHandler = new Ofdpa3GroupHandler();
92 groupHandler.init(deviceId, context);
Charles Chanf9e98652016-09-07 16:54:23 -070093 }
94
95 @Override
Charles Chand1172632017-03-15 17:33:09 -070096 protected boolean requireVlanExtensions() {
97 return false;
98 }
99
100 @Override
Saurav Dasc568c342018-01-25 09:49:01 -0800101 protected boolean shouldRetry() {
102 return false;
103 }
104
105 @Override
Andrea Campanellad980c6d2018-04-30 11:48:55 +0200106 protected boolean supportsUnicastBlackHole() {
107 return true;
108 }
109
110 @Override
Pier Ventre42287df2016-11-09 14:17:26 -0800111 protected void processFilter(FilteringObjective filteringObjective,
112 boolean install,
113 ApplicationId applicationId) {
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700114
115 // Check if filter is intended for pseudowire
116 boolean isPw = isPseudowire(filteringObjective);
117
118 if (isPw) {
Pier Ventre42287df2016-11-09 14:17:26 -0800119 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
120 PortCriterion portCriterion;
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700121
122 VlanIdCriterion innerVlanIdCriterion = null;
123 for (Criterion criterion : filteringObjective.conditions()) {
124 if (criterion.type() == INNER_VLAN_VID) {
125 innerVlanIdCriterion = (VlanIdCriterion) criterion;
126 break;
127 }
128 }
129
Pier Ventre42287df2016-11-09 14:17:26 -0800130 VlanIdCriterion outerVlanIdCriterion = null;
131 // We extract the expected port criterion in the key.
132 portCriterion = (PortCriterion) filteringObjective.key();
133 // We extract the outer vlan id criterion.
134 for (Criterion criterion : filteringObjective.conditions()) {
135 if (criterion.type() == VLAN_VID) {
136 outerVlanIdCriterion = (VlanIdCriterion) criterion;
137 break;
138 }
139 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700140
Pier Ventre42287df2016-11-09 14:17:26 -0800141 // We extract the tunnel id.
142 long tunnelId;
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700143 VlanId egressVlan;
144
Pier Ventre42287df2016-11-09 14:17:26 -0800145 if (filteringObjective.meta() != null &&
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700146 filteringObjective.meta().allInstructions().size() != 2) {
Pier Ventre42287df2016-11-09 14:17:26 -0800147 log.warn("Bad filtering objective from app: {}. Not"
148 + "processing filtering objective", applicationId);
149 fail(filteringObjective, ObjectiveError.BADPARAMS);
150 return;
151 } else if (filteringObjective.meta() != null &&
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700152 filteringObjective.meta().allInstructions().size() == 2 &&
153 filteringObjective.meta().allInstructions().get(0).type() == L2MODIFICATION &&
154 filteringObjective.meta().allInstructions().get(1).type() == L2MODIFICATION) {
155
Pier Ventre42287df2016-11-09 14:17:26 -0800156 L2ModificationInstruction l2instruction = (L2ModificationInstruction)
157 filteringObjective.meta().allInstructions().get(0);
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700158
Pier Ventre42287df2016-11-09 14:17:26 -0800159 if (l2instruction.subtype() != L2SubType.TUNNEL_ID) {
160 log.warn("Bad filtering objective from app: {}. Not"
161 + "processing filtering objective", applicationId);
162 fail(filteringObjective, ObjectiveError.BADPARAMS);
163 return;
164 } else {
165 tunnelId = ((ModTunnelIdInstruction) l2instruction).tunnelId();
166 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700167
168 L2ModificationInstruction vlanInstruction = (L2ModificationInstruction)
169 filteringObjective.meta().allInstructions().get(1);
170
171 if (vlanInstruction.subtype() != L2SubType.VLAN_ID) {
172 log.warn("Bad filtering objective from app: {}. Not"
173 + "processing filtering objective", applicationId);
174 fail(filteringObjective, ObjectiveError.BADPARAMS);
175 return;
176 } else {
177 egressVlan = ((L2ModificationInstruction.ModVlanIdInstruction) vlanInstruction).vlanId();
178 }
Pier Ventre42287df2016-11-09 14:17:26 -0800179 } else {
180 log.warn("Bad filtering objective from app: {}. Not"
181 + "processing filtering objective", applicationId);
182 fail(filteringObjective, ObjectiveError.BADPARAMS);
183 return;
184 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700185
Pier Ventre42287df2016-11-09 14:17:26 -0800186 // Mpls tunnel ids according to the OFDPA manual have to be
187 // in the range [2^17-1, 2^16].
188 tunnelId = MPLS_TUNNEL_ID_BASE | tunnelId;
189 // Sanity check for the filtering objective.
190 if (portCriterion == null ||
191 outerVlanIdCriterion == null ||
192 tunnelId > MPLS_TUNNEL_ID_MAX) {
193 log.warn("Bad filtering objective from app: {}. Not"
194 + "processing filtering objective", applicationId);
195 fail(filteringObjective, ObjectiveError.BADPARAMS);
196 return;
197 }
198 // 0x0000XXXX is UNI interface.
Pier Ventre70d53ba2016-11-17 22:26:29 -0800199 if (portCriterion.port().toLong() > MPLS_UNI_PORT_MAX) {
Sivachidambaram Subramaniana0efdcc2017-06-22 05:26:46 +0530200 log.error("Filtering Objective invalid logical port {}",
Pier Ventre42287df2016-11-09 14:17:26 -0800201 portCriterion.port().toLong());
202 fail(filteringObjective, ObjectiveError.BADPARAMS);
203 return;
204 }
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700205
Pier Ventre42287df2016-11-09 14:17:26 -0800206 // We create the flows.
207 List<FlowRule> pwRules = processPwFilter(portCriterion,
208 innerVlanIdCriterion,
209 outerVlanIdCriterion,
210 tunnelId,
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700211 applicationId,
212 egressVlan
Pier Ventre42287df2016-11-09 14:17:26 -0800213 );
214 // We tag the flow for adding or for removing.
215 for (FlowRule pwRule : pwRules) {
216 log.debug("adding filtering rule in VLAN tables: {} for dev: {}",
217 pwRule, deviceId);
218 ops = install ? ops.add(pwRule) : ops.remove(pwRule);
219 }
220 // We push the filtering rules for the pw.
221 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
222 @Override
223 public void onSuccess(FlowRuleOperations ops) {
224 log.info("Applied {} filtering rules in device {}",
225 ops.stages().get(0).size(), deviceId);
226 pass(filteringObjective);
227 }
228
229 @Override
230 public void onError(FlowRuleOperations ops) {
231 log.info("Failed to apply all filtering rules in dev {}", deviceId);
232 fail(filteringObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
233 }
234 }));
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700235 } else if (isDoubleTagged(filteringObjective)) {
236 processDoubleTaggedFilter(filteringObjective, install, applicationId);
237 } else {
238 // If it is not a pseudo wire flow or double-tagged filter, we fall back
239 // to the OFDPA 2.0 pipeline.
240 super.processFilter(filteringObjective, install, applicationId);
241 }
242 }
Pier Ventre42287df2016-11-09 14:17:26 -0800243
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700244 /**
245 * Configure filtering rules of outer and inner VLAN IDs, and a MAC address.
246 * Filtering happens in three tables (VLAN_TABLE, VLAN_1_TABLE, TMAC_TABLE).
247 *
248 * @param filteringObjective the filtering objective
249 * @param install true to add, false to remove
250 * @param applicationId for application programming this filter
251 */
252 private void processDoubleTaggedFilter(FilteringObjective filteringObjective,
253 boolean install,
254 ApplicationId applicationId) {
255 PortCriterion portCriterion = null;
256 EthCriterion ethCriterion = null;
257 VlanIdCriterion innervidCriterion = null;
258 VlanIdCriterion outerVidCriterion = null;
259 boolean popVlan = false;
260 TrafficTreatment meta = filteringObjective.meta();
261 if (!filteringObjective.key().equals(Criteria.dummy()) &&
262 filteringObjective.key().type() == Criterion.Type.IN_PORT) {
263 portCriterion = (PortCriterion) filteringObjective.key();
264 }
265 if (portCriterion == null) {
266 log.warn("No IN_PORT defined in filtering objective from app: {}" +
267 "Failed to program VLAN tables.", applicationId);
268 return;
269 } else {
270 log.debug("Received filtering objective for dev/port: {}/{}", deviceId,
271 portCriterion.port());
272 }
273
274 // meta should have only one instruction, popVlan.
275 if (meta != null && meta.allInstructions().size() == 1) {
276 L2ModificationInstruction l2Inst = (L2ModificationInstruction) meta.allInstructions().get(0);
277 if (l2Inst.subtype().equals(L2SubType.VLAN_POP)) {
278 popVlan = true;
279 } else {
280 log.warn("Filtering objective can have only VLAN_POP instruction.");
281 return;
282 }
283 } else {
284 log.warn("Filtering objective should have one instruction.");
Pier Ventre42287df2016-11-09 14:17:26 -0800285 return;
286 }
Jonghwan Hyun800d9d02018-04-09 09:40:50 -0700287
288 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
289 for (Criterion criterion : filteringObjective.conditions()) {
290 switch (criterion.type()) {
291 case ETH_DST:
292 case ETH_DST_MASKED:
293 ethCriterion = (EthCriterion) criterion;
294 break;
295 case VLAN_VID:
296 if (innervidCriterion == null) {
297 innervidCriterion = (VlanIdCriterion) criterion;
298 } else {
299 outerVidCriterion = innervidCriterion;
300 innervidCriterion = (VlanIdCriterion) criterion;
301 }
302 break;
303 default:
304 log.warn("Unsupported filter {}", criterion);
305 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
306 return;
307 }
308 }
309
310 if (innervidCriterion == null || outerVidCriterion == null) {
311 log.warn("filtering objective should have two vidCriterion.");
312 return;
313 }
314
315 if (ethCriterion == null || ethCriterion.mac().equals(NONE)) {
316 // NOTE: it is possible that a filtering objective only has vidCriterion
317 log.warn("filtering objective missing dstMac, cannot program TMAC table");
318 return;
319 } else {
320 MacAddress unicastMac = readEthDstFromTreatment(filteringObjective.meta());
321 List<List<FlowRule>> allStages = processEthDstFilter(portCriterion, ethCriterion, innervidCriterion,
322 innervidCriterion.vlanId(), unicastMac,
323 applicationId);
324 for (List<FlowRule> flowRules : allStages) {
325 log.trace("Starting a new flow rule stage for TMAC table flow");
326 ops.newStage();
327
328 for (FlowRule flowRule : flowRules) {
329 log.trace("{} flow rules in TMAC table: {} for dev: {}",
330 (install) ? "adding" : "removing", flowRules, deviceId);
331 if (install) {
332 ops = ops.add(flowRule);
333 } else {
334 // NOTE: Only remove TMAC flow when there is no more enabled port within the
335 // same VLAN on this device if TMAC doesn't support matching on in_port.
336 if (matchInPortTmacTable()
337 || (filteringObjective.meta() != null
338 && filteringObjective.meta().clearedDeferred())) {
339 ops = ops.remove(flowRule);
340 } else {
341 log.debug("Abort TMAC flow removal on {}. Some other ports still share this TMAC flow");
342 }
343 }
344 }
345 }
346 }
347
348 List<FlowRule> rules;
349 rules = processDoubleVlanIdFilter(portCriterion, innervidCriterion,
350 outerVidCriterion, popVlan, applicationId);
351 for (FlowRule flowRule : rules) {
352 log.trace("{} flow rule in VLAN table: {} for dev: {}",
353 (install) ? "adding" : "removing", flowRule, deviceId);
354 ops = install ? ops.add(flowRule) : ops.remove(flowRule);
355 }
356
357 // apply filtering flow rules
358 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
359 @Override
360 public void onSuccess(FlowRuleOperations ops) {
361 log.debug("Applied {} filtering rules in device {}",
362 ops.stages().get(0).size(), deviceId);
363 pass(filteringObjective);
364 }
365
366 @Override
367 public void onError(FlowRuleOperations ops) {
368 log.info("Failed to apply all filtering rules in dev {}", deviceId);
369 fail(filteringObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
370 }
371 }));
372
373 }
374 /**
375 * Internal implementation of processDoubleVlanIdFilter.
376 *
377 * @param portCriterion port on device for which this filter is programmed
378 * @param innerVidCriterion inner vlan
379 * @param outerVidCriterion outer vlan
380 * @param popVlan true if outer vlan header needs to be removed
381 * @param applicationId for application programming this filter
382 * @return flow rules for port-vlan filters
383 */
384 private List<FlowRule> processDoubleVlanIdFilter(PortCriterion portCriterion,
385 VlanIdCriterion innerVidCriterion,
386 VlanIdCriterion outerVidCriterion,
387 boolean popVlan,
388 ApplicationId applicationId) {
389 TrafficSelector.Builder outerSelector = DefaultTrafficSelector.builder();
390 TrafficTreatment.Builder outerTreatment = DefaultTrafficTreatment.builder();
391 TrafficSelector.Builder innerSelector = DefaultTrafficSelector.builder();
392 TrafficTreatment.Builder innerTreatment = DefaultTrafficTreatment.builder();
393
394 VlanId outerVlanId = outerVidCriterion.vlanId();
395 VlanId innerVlanId = innerVidCriterion.vlanId();
396 PortNumber portNumber = portCriterion.port();
397 // Check arguments
398 if (PortNumber.ALL.equals(portNumber)
399 || outerVlanId.equals(VlanId.NONE)
400 || innerVlanId.equals(VlanId.NONE)) {
401 log.warn("Incomplete Filtering Objective. " +
402 "VLAN Table cannot be programmed for {}", deviceId);
403 return ImmutableList.of();
404 } else {
405 outerSelector.matchInPort(portNumber);
406 innerSelector.matchInPort(portNumber);
407 outerTreatment.transition(VLAN_1_TABLE);
408 innerTreatment.transition(TMAC_TABLE);
409
410 if (requireVlanExtensions()) {
411 OfdpaMatchVlanVid ofdpaOuterMatchVlanVid = new OfdpaMatchVlanVid(outerVlanId);
412 outerSelector.extension(ofdpaOuterMatchVlanVid, deviceId);
413 OfdpaMatchVlanVid ofdpaInnerMatchVlanVid = new OfdpaMatchVlanVid(innerVlanId);
414 innerSelector.extension(ofdpaInnerMatchVlanVid, deviceId);
415 } else {
416 outerSelector.matchVlanId(outerVlanId);
417 innerSelector.matchVlanId(innerVlanId);
418 }
419
420 innerSelector.extension(new Ofdpa3MatchOvid(outerVlanId), deviceId);
421 outerTreatment.extension(new Ofdpa3SetOvid(outerVlanId), deviceId);
422 if (popVlan) {
423 outerTreatment.popVlan();
424 }
425 }
426 FlowRule outerRule = DefaultFlowRule.builder()
427 .forDevice(deviceId)
428 .withSelector(outerSelector.build())
429 .withTreatment(outerTreatment.build())
430 .withPriority(DEFAULT_PRIORITY)
431 .fromApp(applicationId)
432 .makePermanent()
433 .forTable(VLAN_TABLE)
434 .build();
435 FlowRule innerRule = DefaultFlowRule.builder()
436 .forDevice(deviceId)
437 .withSelector(innerSelector.build())
438 .withTreatment(innerTreatment.build())
439 .withPriority(DEFAULT_PRIORITY)
440 .fromApp(applicationId)
441 .makePermanent()
442 .forTable(VLAN_1_TABLE)
443 .build();
444
445 return ImmutableList.of(outerRule, innerRule);
446 }
447
448 /**
449 * Determines if the filtering objective will be used for double-tagged packets.
450 *
451 * @param fob Filtering objective
452 * @return True if the objective was created for double-tagged packets, false otherwise.
453 */
454 private boolean isDoubleTagged(FilteringObjective fob) {
455 return fob.meta() != null &&
456 fob.meta().allInstructions().stream().anyMatch(inst -> inst.type() == L2MODIFICATION
457 && ((L2ModificationInstruction) inst).subtype() == L2SubType.VLAN_POP) &&
458 fob.conditions().stream().filter(criterion -> criterion.type() == VLAN_VID).count() == 2;
Pier Ventre42287df2016-11-09 14:17:26 -0800459 }
460
461 /**
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700462 * Determines if the filtering objective will be used for a pseudowire.
463 *
464 * @param filteringObjective
465 * @return True if objective was created for a pseudowire, false otherwise.
466 */
467 private boolean isPseudowire(FilteringObjective filteringObjective) {
468
469
470 if (filteringObjective.meta() != null) {
471
472 TrafficTreatment treatment = filteringObjective.meta();
473 for (Instruction instr : treatment.immediate()) {
474 if (instr.type().equals(Instruction.Type.L2MODIFICATION)) {
475
476 L2ModificationInstruction l2Instr = (L2ModificationInstruction) instr;
477 if (l2Instr.subtype().equals(L2SubType.TUNNEL_ID)) {
478 return true;
479 }
480 }
481 }
482 }
483
484 return false;
485 }
486
487 /**
Pier Ventre42287df2016-11-09 14:17:26 -0800488 * Method to process the pw related filtering objectives.
489 *
490 * @param portCriterion the in port match
491 * @param innerVlanIdCriterion the inner vlan match
492 * @param outerVlanIdCriterion the outer vlan match
493 * @param tunnelId the tunnel id
494 * @param applicationId the application id
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700495 * @param egressVlan the vlan to modify, was passed in metadata
Pier Ventre42287df2016-11-09 14:17:26 -0800496 * @return a list of flow rules to install
497 */
498 private List<FlowRule> processPwFilter(PortCriterion portCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700499 VlanIdCriterion innerVlanIdCriterion,
500 VlanIdCriterion outerVlanIdCriterion,
501 long tunnelId,
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700502 ApplicationId applicationId,
503 VlanId egressVlan) {
504
Pier Ventre42287df2016-11-09 14:17:26 -0800505 FlowRule vlan1FlowRule;
506 int mplsLogicalPort = ((int) portCriterion.port().toLong());
507 // We have to match on the inner vlan and outer vlan at the same time.
508 // Ofdpa supports this through the OVID meta-data type.
Pier Ventre42287df2016-11-09 14:17:26 -0800509
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700510 ImmutableList<FlowRule> toReturn;
511
512 // pseudowire configured with double tagged vlans
513 if (!(innerVlanIdCriterion.vlanId().equals(VlanId.NONE))
514 && !(outerVlanIdCriterion.vlanId().equals(VlanId.NONE))) {
515
516 log.info("Installing filter objective for double tagged CE for tunnel {}", tunnelId);
517
518 TrafficSelector.Builder vlan1Selector = DefaultTrafficSelector.builder()
519 .matchInPort(portCriterion.port())
520 .matchVlanId(innerVlanIdCriterion.vlanId())
521 .extension(new Ofdpa3MatchOvid(outerVlanIdCriterion.vlanId()), deviceId);
522 // TODO understand for the future how to manage the vlan rewrite.
523 TrafficTreatment.Builder vlan1Treatment = DefaultTrafficTreatment.builder()
524 .pushVlan()
525 .setVlanId(egressVlan)
526 .extension(new Ofdpa3SetMplsType(VPWS), deviceId)
527 .extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId)
528 .setTunnelId(tunnelId)
529 .transition(MPLS_L2_PORT_FLOW_TABLE);
530 vlan1FlowRule = DefaultFlowRule.builder()
531 .forDevice(deviceId)
532 .withSelector(vlan1Selector.build())
533 .withTreatment(vlan1Treatment.build())
534 .withPriority(DEFAULT_PRIORITY)
535 .fromApp(applicationId)
536 .makePermanent()
537 .forTable(VLAN_1_TABLE)
538 .build();
539 // Finally we create the flow rule for the vlan table.
540 FlowRule vlanFlowRule;
541 // We have to match on the outer vlan.
542 TrafficSelector.Builder vlanSelector = DefaultTrafficSelector.builder()
543 .matchInPort(portCriterion.port())
544 .matchVlanId(outerVlanIdCriterion.vlanId());
545 // TODO understand for the future how to manage the vlan rewrite.
546 TrafficTreatment.Builder vlanTreatment = DefaultTrafficTreatment.builder()
547 .popVlan()
548 .extension(new Ofdpa3SetOvid(outerVlanIdCriterion.vlanId()), deviceId)
549 .transition(VLAN_1_TABLE);
550 vlanFlowRule = DefaultFlowRule.builder()
551 .forDevice(deviceId)
552 .withSelector(vlanSelector.build())
553 .withTreatment(vlanTreatment.build())
554 .withPriority(DEFAULT_PRIORITY)
555 .fromApp(applicationId)
556 .makePermanent()
557 .forTable(VLAN_TABLE)
558 .build();
559
560 return ImmutableList.of(vlan1FlowRule, vlanFlowRule);
561 } else if (!(innerVlanIdCriterion.vlanId().equals(VlanId.NONE))
562 && (outerVlanIdCriterion.vlanId().equals(VlanId.NONE))) {
563
564 log.info("Installing filter objective for single tagged CE for tunnel {}", tunnelId);
565
566 TrafficSelector.Builder singleVlanSelector = DefaultTrafficSelector.builder()
567 .matchInPort(portCriterion.port())
568 .matchVlanId(innerVlanIdCriterion.vlanId());
569
570 TrafficTreatment.Builder singleVlanTreatment = DefaultTrafficTreatment.builder()
Charles Chanf45552d2018-07-13 18:08:33 -0700571 // FIXME PW VLAN translation is not supported on Dune
572 // Need to explore doing that in egress table later if there is a requirement
573 // .setVlanId(egressVlan)
Andreas Pantelopoulos27532cd2017-10-23 12:18:25 -0700574 .extension(new Ofdpa3SetMplsType(VPWS), deviceId)
575 .extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId)
576 .setTunnelId(tunnelId)
577 .transition(MPLS_L2_PORT_FLOW_TABLE);
578
579 vlan1FlowRule = DefaultFlowRule.builder()
580 .forDevice(deviceId)
581 .withSelector(singleVlanSelector.build())
582 .withTreatment(singleVlanTreatment.build())
583 .withPriority(DEFAULT_PRIORITY)
584 .fromApp(applicationId)
585 .makePermanent()
586 .forTable(VLAN_TABLE)
587 .build();
588
589 return ImmutableList.of(vlan1FlowRule);
590 } else if ((innerVlanIdCriterion.vlanId().equals(VlanId.NONE))
591 && (outerVlanIdCriterion.vlanId().equals(VlanId.NONE))) {
592
593 TrafficSelector.Builder singleVlanSelector = DefaultTrafficSelector.builder()
594 .matchInPort(portCriterion.port())
595 .matchVlanId(innerVlanIdCriterion.vlanId());
596
597 TrafficTreatment.Builder singleVlanTreatment = DefaultTrafficTreatment.builder()
598 .extension(new Ofdpa3SetMplsType(VPWS), deviceId)
599 .extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId)
600 .setTunnelId(tunnelId)
601 .transition(MPLS_L2_PORT_FLOW_TABLE);
602
603 vlan1FlowRule = DefaultFlowRule.builder()
604 .forDevice(deviceId)
605 .withSelector(singleVlanSelector.build())
606 .withTreatment(singleVlanTreatment.build())
607 .withPriority(DEFAULT_PRIORITY)
608 .fromApp(applicationId)
609 .makePermanent()
610 .forTable(VLAN_TABLE)
611 .build();
612
613 return ImmutableList.of(vlan1FlowRule);
614 } else {
615 // failure...
616 return Collections.emptyList();
617 }
Pier Ventre42287df2016-11-09 14:17:26 -0800618 }
619
620 @Override
Charles Chanf9e98652016-09-07 16:54:23 -0700621 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
Saurav Dasa4020382018-02-14 14:14:54 -0800622 // if its not-bos, we go to MPLS_TYPE_TABLE regardless of whether we pop or swap
623 // if it is bos, we go to MPLS_TYPE_TABLE only if we swap
624 if (isNotMplsBos(fwd.selector())
625 || (isMplsBos(fwd.selector()) && !isMplsPop(fwd))) {
Pier Ventre140a8942016-11-02 07:26:38 -0700626 return processEthTypeSpecificInternal(fwd, true, MPLS_TYPE_TABLE);
627 }
Saurav Dasa4020382018-02-14 14:14:54 -0800628 // if it is bos, and we pop, we go to MPLS_L3_TYPE_TABLE
Pier Ventre140a8942016-11-02 07:26:38 -0700629 return processEthTypeSpecificInternal(fwd, true, MPLS_L3_TYPE_TABLE);
Charles Chanf9e98652016-09-07 16:54:23 -0700630 }
Pier Ventre42287df2016-11-09 14:17:26 -0800631
632 @Override
633 protected Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
634 // We use the tunnel id to identify pw related flows.
Pier Ventre70d53ba2016-11-17 22:26:29 -0800635 // Looking for the fwd objective of the initiation.
Pier Ventre42287df2016-11-09 14:17:26 -0800636 TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) fwd.selector()
637 .getCriterion(TUNNEL_ID);
638 if (tunnelIdCriterion != null) {
Pier Ventre70d53ba2016-11-17 22:26:29 -0800639 return processInitPwVersatile(fwd);
640 }
641 // Looking for the fwd objective of the termination.
642 ModTunnelIdInstruction modTunnelIdInstruction = getModTunnelIdInstruction(fwd.treatment());
643 OutputInstruction outputInstruction = getOutputInstruction(fwd.treatment());
644 if (modTunnelIdInstruction != null && outputInstruction != null) {
645 return processTermPwVersatile(fwd, modTunnelIdInstruction, outputInstruction);
Pier Ventre42287df2016-11-09 14:17:26 -0800646 }
647 // If it is not a pseudo wire flow we fall back
648 // to the OFDPA 2.0 pipeline.
649 return super.processVersatile(fwd);
650 }
651
Pier Ventre70d53ba2016-11-17 22:26:29 -0800652 private Collection<FlowRule> processTermPwVersatile(ForwardingObjective forwardingObjective,
653 ModTunnelIdInstruction modTunnelIdInstruction,
654 OutputInstruction outputInstruction) {
655 TrafficTreatment.Builder flowTreatment;
656 TrafficSelector.Builder flowSelector;
657 // We divide the mpls actions from the tunnel actions. We need
658 // this to order the actions in the final treatment.
659 TrafficTreatment.Builder mplsTreatment = DefaultTrafficTreatment.builder();
660 createMplsTreatment(forwardingObjective.treatment(), mplsTreatment);
661 // The match of the forwarding objective is ready to go.
662 flowSelector = DefaultTrafficSelector.builder(forwardingObjective.selector());
663 // We verify the tunnel id and mpls port are correct.
664 long tunnelId = MPLS_TUNNEL_ID_BASE | modTunnelIdInstruction.tunnelId();
665 if (tunnelId > MPLS_TUNNEL_ID_MAX) {
666 log.error("Pw Versatile Forwarding Objective must include tunnel id < {}",
667 MPLS_TUNNEL_ID_MAX);
668 fail(forwardingObjective, ObjectiveError.BADPARAMS);
669 return Collections.emptySet();
670 }
671 // 0x0002XXXX is NNI interface.
672 int mplsLogicalPort = ((int) outputInstruction.port().toLong()) | MPLS_NNI_PORT_BASE;
673 if (mplsLogicalPort > MPLS_NNI_PORT_MAX) {
674 log.error("Pw Versatile Forwarding Objective invalid logical port {}",
675 mplsLogicalPort);
676 fail(forwardingObjective, ObjectiveError.BADPARAMS);
677 return Collections.emptySet();
678 }
679 // Next id cannot be null.
680 if (forwardingObjective.nextId() == null) {
681 log.error("Pw Versatile Forwarding Objective must contain nextId ",
682 forwardingObjective.nextId());
683 fail(forwardingObjective, ObjectiveError.BADPARAMS);
684 return Collections.emptySet();
685 }
686 // We retrieve the l2 interface group and point the mpls
687 // flow to this.
688 NextGroup next = getGroupForNextObjective(forwardingObjective.nextId());
689 if (next == null) {
690 log.warn("next-id:{} not found in dev:{}", forwardingObjective.nextId(), deviceId);
691 fail(forwardingObjective, ObjectiveError.GROUPMISSING);
692 return Collections.emptySet();
693 }
694 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
695 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
696 if (group == null) {
697 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
698 gkeys.get(0).peekFirst(), forwardingObjective.nextId(), deviceId);
699 fail(forwardingObjective, ObjectiveError.GROUPMISSING);
700 return Collections.emptySet();
701 }
702 // We prepare the treatment for the mpls flow table.
703 // The order of the actions has to be strictly this
704 // according to the OFDPA 2.0 specification.
705 flowTreatment = DefaultTrafficTreatment.builder(mplsTreatment.build());
706 flowTreatment.extension(new Ofdpa3PopCw(), deviceId);
Pier Luigi3bfe32c2017-01-30 09:47:36 -0800707 // Even though the specification and the xml/json files
708 // specify is allowed, the switch rejects the flow. In the
709 // OFDPA 3.0 EA0 version was necessary
710 //flowTreatment.popVlan();
Pier Ventre70d53ba2016-11-17 22:26:29 -0800711 flowTreatment.extension(new Ofdpa3PopL2Header(), deviceId);
712 flowTreatment.setTunnelId(tunnelId);
713 flowTreatment.extension(new Ofdpa3SetMplsL2Port(mplsLogicalPort), deviceId);
714 flowTreatment.extension(new Ofdpa3SetMplsType(VPWS), deviceId);
715 flowTreatment.transition(MPLS_TYPE_TABLE);
716 flowTreatment.deferred().group(group.id());
717 // We prepare the flow rule for the mpls table.
718 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
719 .fromApp(forwardingObjective.appId())
720 .withPriority(forwardingObjective.priority())
721 .forDevice(deviceId)
722 .withSelector(flowSelector.build())
723 .withTreatment(flowTreatment.build())
724 .makePermanent()
725 .forTable(MPLS_TABLE_1);
726 return Collections.singletonList(ruleBuilder.build());
727 }
728
Pier Ventre42287df2016-11-09 14:17:26 -0800729 /**
730 * Helper method to process the pw forwarding objectives.
731 *
732 * @param forwardingObjective the fw objective to process
733 * @return a singleton list of flow rule
734 */
Pier Ventre70d53ba2016-11-17 22:26:29 -0800735 private Collection<FlowRule> processInitPwVersatile(ForwardingObjective forwardingObjective) {
Pier Ventre42287df2016-11-09 14:17:26 -0800736 // We retrieve the matching criteria for mpls l2 port.
737 TunnelIdCriterion tunnelIdCriterion = (TunnelIdCriterion) forwardingObjective.selector()
738 .getCriterion(TUNNEL_ID);
739 PortCriterion portCriterion = (PortCriterion) forwardingObjective.selector()
740 .getCriterion(IN_PORT);
741 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
742 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
743 int mplsLogicalPort;
744 long tunnelId;
745 // Mpls tunnel ids according to the OFDPA manual have to be
746 // in the range [2^17-1, 2^16].
747 tunnelId = MPLS_TUNNEL_ID_BASE | tunnelIdCriterion.tunnelId();
748 if (tunnelId > MPLS_TUNNEL_ID_MAX) {
749 log.error("Pw Versatile Forwarding Objective must include tunnel id < {}",
750 MPLS_TUNNEL_ID_MAX);
751 fail(forwardingObjective, ObjectiveError.BADPARAMS);
752 return Collections.emptySet();
753 }
754 // Port has not been null.
755 if (portCriterion == null) {
756 log.error("Pw Versatile Forwarding Objective must include port");
757 fail(forwardingObjective, ObjectiveError.BADPARAMS);
758 return Collections.emptySet();
759 }
760 // 0x0000XXXX is UNI interface.
Pier Ventre70d53ba2016-11-17 22:26:29 -0800761 if (portCriterion.port().toLong() > MPLS_UNI_PORT_MAX) {
Pier Ventre42287df2016-11-09 14:17:26 -0800762 log.error("Pw Versatile Forwarding Objective invalid logical port {}",
763 portCriterion.port().toLong());
764 fail(forwardingObjective, ObjectiveError.BADPARAMS);
765 return Collections.emptySet();
766 }
767 mplsLogicalPort = ((int) portCriterion.port().toLong());
768 if (forwardingObjective.nextId() == null) {
769 log.error("Pw Versatile Forwarding Objective must contain nextId ",
770 forwardingObjective.nextId());
771 fail(forwardingObjective, ObjectiveError.BADPARAMS);
772 return Collections.emptySet();
773 }
774 // We don't expect a treatment.
775 if (forwardingObjective.treatment() != null &&
776 !forwardingObjective.treatment().equals(DefaultTrafficTreatment.emptyTreatment())) {
777 log.error("Pw Versatile Forwarding Objective cannot contain a treatment ",
778 forwardingObjective.nextId());
779 fail(forwardingObjective, ObjectiveError.BADPARAMS);
780 return Collections.emptySet();
781 }
782 // We retrieve the l2 vpn group and point the mpls
783 // l2 port to this.
784 NextGroup next = getGroupForNextObjective(forwardingObjective.nextId());
785 if (next == null) {
786 log.warn("next-id:{} not found in dev:{}", forwardingObjective.nextId(), deviceId);
787 fail(forwardingObjective, ObjectiveError.GROUPMISSING);
788 return Collections.emptySet();
789 }
790 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
791 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
792 if (group == null) {
793 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
794 gkeys.get(0).peekFirst(), forwardingObjective.nextId(), deviceId);
795 fail(forwardingObjective, ObjectiveError.GROUPMISSING);
796 return Collections.emptySet();
797 }
798 // We prepare the flow rule for the mpls l2 port table.
799 selector.matchTunnelId(tunnelId);
800 selector.extension(new Ofdpa3MatchMplsL2Port(mplsLogicalPort), deviceId);
801 // This should not be necessary but without we receive an error
802 treatment.extension(new Ofdpa3SetQosIndex(0), deviceId);
803 treatment.transition(MPLS_L2_PORT_PCP_TRUST_FLOW_TABLE);
804 treatment.deferred().group(group.id());
805 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
806 .fromApp(forwardingObjective.appId())
807 .withPriority(MPLS_L2_PORT_PRIORITY)
808 .forDevice(deviceId)
809 .withSelector(selector.build())
810 .withTreatment(treatment.build())
811 .makePermanent()
812 .forTable(MPLS_L2_PORT_FLOW_TABLE);
813 return Collections.singletonList(ruleBuilder.build());
814 }
Pier Ventre70d53ba2016-11-17 22:26:29 -0800815
816 /**
817 * Utility function to get the mod tunnel id instruction
818 * if present.
819 *
820 * @param treatment the treatment to analyze
821 * @return the mod tunnel id instruction if present,
822 * otherwise null
823 */
824 private ModTunnelIdInstruction getModTunnelIdInstruction(TrafficTreatment treatment) {
Charles Chan3bb11702017-02-16 14:58:58 -0800825 if (treatment == null) {
826 return null;
827 }
828
Pier Ventre70d53ba2016-11-17 22:26:29 -0800829 L2ModificationInstruction l2ModificationInstruction;
830 for (Instruction instruction : treatment.allInstructions()) {
831 if (instruction.type() == L2MODIFICATION) {
832 l2ModificationInstruction = (L2ModificationInstruction) instruction;
833 if (l2ModificationInstruction.subtype() == L2SubType.TUNNEL_ID) {
834 return (ModTunnelIdInstruction) l2ModificationInstruction;
835 }
836 }
837 }
838 return null;
839 }
840
841 /**
842 * Utility function to get the output instruction
843 * if present.
844 *
845 * @param treatment the treatment to analyze
846 * @return the output instruction if present,
847 * otherwise null
848 */
849 private OutputInstruction getOutputInstruction(TrafficTreatment treatment) {
Saurav Das018605f2017-02-18 14:05:44 -0800850 if (treatment == null) {
851 return null;
852 }
853
Pier Ventre70d53ba2016-11-17 22:26:29 -0800854 for (Instruction instruction : treatment.allInstructions()) {
855 if (instruction.type() == Instruction.Type.OUTPUT) {
856 return (OutputInstruction) instruction;
857 }
858 }
859 return null;
860 }
861
862 /**
863 * Helper method for dividing the tunnel instructions from the mpls
864 * instructions.
865 *
866 * @param treatment the treatment to analyze
867 * @param mplsTreatment the mpls treatment builder
868 */
869 private void createMplsTreatment(TrafficTreatment treatment,
870 TrafficTreatment.Builder mplsTreatment) {
871
872 for (Instruction ins : treatment.allInstructions()) {
873
874 if (ins.type() == Instruction.Type.L2MODIFICATION) {
875 L2ModificationInstruction l2ins = (L2ModificationInstruction) ins;
876 switch (l2ins.subtype()) {
877 // These instructions have to go in the mpls
878 // treatment.
879 case TUNNEL_ID:
880 break;
881 case DEC_MPLS_TTL:
882 case MPLS_POP:
883 mplsTreatment.add(ins);
884 break;
885 default:
886 log.warn("Driver does not handle this type of TrafficTreatment"
887 + " instruction in nextObjectives: {} - {}",
888 ins.type(), ins);
889 break;
890 }
891 } else if (ins.type() == Instruction.Type.OUTPUT) {
892 break;
893 } else if (ins.type() == Instruction.Type.L3MODIFICATION) {
894 // We support partially the l3 instructions.
895 L3ModificationInstruction l3ins = (L3ModificationInstruction) ins;
896 switch (l3ins.subtype()) {
897 case TTL_IN:
898 mplsTreatment.add(ins);
899 break;
900 default:
901 log.warn("Driver does not handle this type of TrafficTreatment"
902 + " instruction in nextObjectives: {} - {}",
903 ins.type(), ins);
904 }
905
906 } else {
907 log.warn("Driver does not handle this type of TrafficTreatment"
908 + " instruction in nextObjectives: {} - {}",
909 ins.type(), ins);
910 }
911 }
912 }
Charles Chanf9e98652016-09-07 16:54:23 -0700913}