blob: 898ec448829af83ad41aeae73b56c35a7b0769d6 [file] [log] [blame]
Thomas Vachuska58de4162015-09-10 16:15:33 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Thomas Vachuska58de4162015-09-10 16:15:33 -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 */
Yi Tsengef19de12017-04-24 11:33:05 -070016package org.onosproject.driver.pipeline.ofdpa;
Saurav Das558afec2015-05-31 17:12:48 -070017
Charles Chan5270ed02016-01-30 23:22:37 -080018import com.google.common.collect.ImmutableList;
Charles Chan5b9df8d2016-03-28 22:21:40 -070019import com.google.common.collect.ImmutableSet;
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -070020import org.onlab.packet.EthType;
Saurav Das8a0732e2015-11-20 15:27:53 -080021import org.onlab.packet.Ethernet;
Julia Ferguson65428c32017-08-10 18:15:24 +000022import org.onlab.packet.IpAddress;
Flavio Castroe10fa242016-01-15 12:43:51 -080023import org.onlab.packet.IpPrefix;
Charles Chan45b69ab2018-03-02 15:41:41 -080024import org.onlab.packet.MacAddress;
Saurav Das2857f382015-11-03 14:39:27 -080025import org.onlab.packet.VlanId;
26import org.onosproject.core.ApplicationId;
Yi Tsengfa394de2017-02-01 11:26:40 -080027import org.onosproject.core.GroupId;
Andrea Campanella7c977b92018-05-30 21:39:45 -070028import org.onosproject.net.ConnectPoint;
Andreas Pantelopoulos89369462018-05-30 14:30:24 -070029import org.onosproject.net.DeviceId;
Andrea Campanella7c977b92018-05-30 21:39:45 -070030import org.onosproject.driver.extensions.OfdpaMatchActsetOutput;
31import org.onosproject.net.Host;
Saurav Das2857f382015-11-03 14:39:27 -080032import org.onosproject.net.Port;
33import org.onosproject.net.PortNumber;
Saurav Das8a0732e2015-11-20 15:27:53 -080034import org.onosproject.net.behaviour.NextGroup;
Charles Chan425854b2016-04-11 15:32:12 -070035import org.onosproject.net.behaviour.PipelinerContext;
Saurav Das558afec2015-05-31 17:12:48 -070036import org.onosproject.net.flow.DefaultFlowRule;
37import org.onosproject.net.flow.DefaultTrafficSelector;
38import org.onosproject.net.flow.DefaultTrafficTreatment;
39import org.onosproject.net.flow.FlowRule;
40import org.onosproject.net.flow.FlowRuleOperations;
41import org.onosproject.net.flow.FlowRuleOperationsContext;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
Saurav Das4ce45962015-11-24 23:21:05 -080044import org.onosproject.net.flow.criteria.Criteria;
Saurav Das8a0732e2015-11-20 15:27:53 -080045import org.onosproject.net.flow.criteria.Criterion;
Saurav Das4ce45962015-11-24 23:21:05 -080046import org.onosproject.net.flow.criteria.EthCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080047import org.onosproject.net.flow.criteria.EthTypeCriterion;
48import org.onosproject.net.flow.criteria.IPCriterion;
Pier Ventree0ae7a32016-11-23 09:57:42 -080049import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion;
50import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080051import org.onosproject.net.flow.criteria.MplsBosCriterion;
52import org.onosproject.net.flow.criteria.MplsCriterion;
Saurav Das2857f382015-11-03 14:39:27 -080053import org.onosproject.net.flow.criteria.PortCriterion;
54import org.onosproject.net.flow.criteria.VlanIdCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080055import org.onosproject.net.flow.instructions.Instruction;
Andrea Campanella7c977b92018-05-30 21:39:45 -070056import org.onosproject.net.flow.instructions.Instructions;
Saurav Das52025962016-01-28 22:30:01 -080057import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Charles Chanf76de302018-06-15 18:54:18 -070058import org.onosproject.net.flow.instructions.Instructions.NoActionInstruction;
Andrea Campanella7c977b92018-05-30 21:39:45 -070059import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Charles Chan40132b32017-01-22 00:19:37 -080060import org.onosproject.net.flow.instructions.L3ModificationInstruction;
Andrea Campanella7c977b92018-05-30 21:39:45 -070061import org.onosproject.net.flowobjective.FilteringObjective;
Saurav Das8a0732e2015-11-20 15:27:53 -080062import org.onosproject.net.flowobjective.ForwardingObjective;
63import org.onosproject.net.flowobjective.ObjectiveError;
Charles Chan0f43e472017-02-14 14:00:16 -080064import org.onosproject.net.group.DefaultGroupBucket;
65import org.onosproject.net.group.DefaultGroupDescription;
66import org.onosproject.net.group.DefaultGroupKey;
Saurav Das8a0732e2015-11-20 15:27:53 -080067import org.onosproject.net.group.Group;
Charles Chan0f43e472017-02-14 14:00:16 -080068import org.onosproject.net.group.GroupBucket;
69import org.onosproject.net.group.GroupBuckets;
70import org.onosproject.net.group.GroupDescription;
Saurav Das8a0732e2015-11-20 15:27:53 -080071import org.onosproject.net.group.GroupKey;
Andrea Campanella7c977b92018-05-30 21:39:45 -070072import org.onosproject.net.host.HostService;
Charles Chanf57a8252016-06-29 19:12:37 -070073import org.onosproject.net.packet.PacketPriority;
Saurav Das558afec2015-05-31 17:12:48 -070074import org.slf4j.Logger;
75
Jonathan Hart855179c2016-04-26 07:40:04 -070076import java.util.ArrayList;
77import java.util.Collection;
78import java.util.Collections;
79import java.util.Deque;
Andrea Campanella7c977b92018-05-30 21:39:45 -070080import java.util.HashSet;
Jonathan Hart855179c2016-04-26 07:40:04 -070081import java.util.List;
Charles Chan0f43e472017-02-14 14:00:16 -080082import java.util.Objects;
Andreas Pantelopoulos89369462018-05-30 14:30:24 -070083import java.util.Queue;
84import java.util.concurrent.ConcurrentLinkedQueue;
85import java.util.concurrent.Executors;
86import java.util.concurrent.ScheduledExecutorService;
87import java.util.concurrent.TimeUnit;
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -070088import java.util.concurrent.locks.ReentrantLock;
Andrea Campanella7c977b92018-05-30 21:39:45 -070089import java.util.Optional;
Jonathan Hart855179c2016-04-26 07:40:04 -070090
Pier Ventree0ae7a32016-11-23 09:57:42 -080091import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
Charles Chand05f54b2017-02-13 11:56:54 -080092import static org.onlab.packet.MacAddress.BROADCAST;
93import static org.onlab.packet.MacAddress.NONE;
Andreas Pantelopoulos89369462018-05-30 14:30:24 -070094import static org.onlab.util.Tools.groupedThreads;
Yi Tsengef19de12017-04-24 11:33:05 -070095import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*;
Andrea Campanella7c977b92018-05-30 21:39:45 -070096import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
97import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
Jonathan Hart855179c2016-04-26 07:40:04 -070098import static org.slf4j.LoggerFactory.getLogger;
99
Saurav Das558afec2015-05-31 17:12:48 -0700100
101/**
Charles Chan40132b32017-01-22 00:19:37 -0800102 * Driver for software switch emulation of the OFDPA pipeline.
Saurav Das52025962016-01-28 22:30:01 -0800103 * The software switch is the CPqD OF 1.3 switch. Unfortunately the CPqD switch
104 * does not handle vlan tags and mpls labels simultaneously, which requires us
105 * to do some workarounds in the driver. This driver is meant for the use of
106 * the cpqd switch when MPLS is required. As a result this driver works only
107 * on incoming untagged packets.
Saurav Das558afec2015-05-31 17:12:48 -0700108 */
Charles Chan361154b2016-03-24 10:23:39 -0700109public class CpqdOfdpa2Pipeline extends Ofdpa2Pipeline {
Saurav Das558afec2015-05-31 17:12:48 -0700110
111 private final Logger log = getLogger(getClass());
112
Andrea Campanella7c977b92018-05-30 21:39:45 -0700113 private static final int EGRESS_VLAN_FLOW_TABLE_IN_INGRESS = 31;
114 private static final int UNICAST_ROUTING_TABLE_1 = 32;
Charles Chan40132b32017-01-22 00:19:37 -0800115 /**
Charles Chan0f43e472017-02-14 14:00:16 -0800116 * Table that determines whether VLAN is popped before punting to controller.
117 * <p>
118 * This is a non-OFDPA table to emulate OFDPA packet in behavior.
119 * VLAN will be popped before punting if the VLAN is internally assigned.
120 * <p>
121 * Also note that 63 is the max table number in CpqD.
122 */
123 private static final int PUNT_TABLE = 63;
124
125 /**
126 * A static indirect group that pop vlan and punt to controller.
127 * <p>
128 * The purpose of using a group instead of immediate action is that this
129 * won't affect another copy on the data plane when write action exists.
130 */
131 private static final int POP_VLAN_PUNT_GROUP_ID = 0xc0000000;
132
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700133 /**
134 * Executor for group checker thread that checks pop vlan punt group.
135 */
136 private ScheduledExecutorService groupChecker;
137
138 /**
139 * Queue for passing pop vlan punt group flow rules to the GroupChecker thread.
140 */
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700141 private Queue<FlowRule> flowRuleQueue;
142
143 /**
144 * Lock used in synchronizing driver thread with groupCheckerThread.
145 */
146 private ReentrantLock groupCheckerLock;
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700147
Charles Chan053b1cb2017-03-22 16:56:35 -0700148 @Override
149 protected boolean requireVlanExtensions() {
150 return false;
151 }
152
Charles Chan0f43e472017-02-14 14:00:16 -0800153 /**
Charles Chan40132b32017-01-22 00:19:37 -0800154 * Determines whether this pipeline support copy ttl instructions or not.
155 *
156 * @return true if copy ttl instructions are supported
157 */
158 protected boolean supportCopyTtl() {
159 return true;
160 }
161
Charles Chan0f43e472017-02-14 14:00:16 -0800162 /**
163 * Determines whether this pipeline support push mpls to vlan-tagged packets or not.
164 * <p>
165 * If not support, pop vlan before push entering unicast and mpls table.
166 * Side effect: HostService learns redundant hosts with same MAC but
167 * different VLAN. No known side effect on the network reachability.
168 *
169 * @return true if push mpls to vlan-tagged packets is supported
170 */
171 protected boolean supportTaggedMpls() {
172 return false;
173 }
174
175 /**
176 * Determines whether this pipeline support punt action in group bucket.
177 *
178 * @return true if punt action in group bucket is supported
179 */
180 protected boolean supportPuntGroup() {
181 return false;
182 }
183
Charles Chan425854b2016-04-11 15:32:12 -0700184 @Override
Charles Chan40132b32017-01-22 00:19:37 -0800185 protected void initDriverId() {
Charles Chan425854b2016-04-11 15:32:12 -0700186 driverId = coreService.registerApplication(
187 "org.onosproject.driver.CpqdOfdpa2Pipeline");
Charles Chan40132b32017-01-22 00:19:37 -0800188 }
Charles Chan425854b2016-04-11 15:32:12 -0700189
Charles Chan40132b32017-01-22 00:19:37 -0800190 @Override
191 protected void initGroupHander(PipelinerContext context) {
192 groupHandler = new CpqdOfdpa2GroupHandler();
193 groupHandler.init(deviceId, context);
Charles Chan425854b2016-04-11 15:32:12 -0700194 }
195
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700196 @Override
197 public void init(DeviceId deviceId, PipelinerContext context) {
198
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700199 if (supportPuntGroup()) {
200 // create a new executor at each init and a new empty queue
201 groupChecker = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/driver",
202 "cpqd-ofdpa-%d", log));
203 flowRuleQueue = new ConcurrentLinkedQueue<>();
204 groupCheckerLock = new ReentrantLock();
205 groupChecker.scheduleAtFixedRate(new PopVlanPuntGroupChecker(), 20, 50, TimeUnit.MILLISECONDS);
206 super.init(deviceId, context);
207 }
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700208 }
Andrea Campanella7c977b92018-05-30 21:39:45 -0700209 protected void processFilter(FilteringObjective filteringObjective,
210 boolean install,
211 ApplicationId applicationId) {
212 if (isDoubleTagged(filteringObjective)) {
213 processDoubleTaggedFilter(filteringObjective, install, applicationId);
214 } else {
215 // If it is not a double-tagged filter, we fall back
216 // to the OFDPA 2.0 pipeline.
217 super.processFilter(filteringObjective, install, applicationId);
218 }
219 }
220
221 /**
222 * Determines if the filtering objective will be used for double-tagged packets.
223 *
224 * @param fob Filtering objective
225 * @return True if the objective was created for double-tagged packets, false otherwise.
226 */
227 private boolean isDoubleTagged(FilteringObjective fob) {
228 return fob.meta() != null &&
229 fob.meta().allInstructions().stream().anyMatch(inst -> inst.type() == L2MODIFICATION
230 && ((L2ModificationInstruction) inst).subtype() ==
231 L2ModificationInstruction.L2SubType.VLAN_POP) &&
232 fob.conditions().stream().filter(criterion -> criterion.type() == VLAN_VID).count() == 2;
233 }
234
235 /**
236 * Determines if the forwarding objective will be used for double-tagged packets.
237 *
238 * @param fwd Forwarding objective
239 * @return True if the objective was created for double-tagged packets, false otherwise.
240 */
241 private boolean isDoubleTagged(ForwardingObjective fwd) {
242 if (fwd.nextId() != null) {
243 NextGroup next = getGroupForNextObjective(fwd.nextId());
244 if (next != null) {
245 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
246 // we only need the top level group's key
247 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
248 if (group != null) {
249 int groupId = group.id().id();
250 if (((groupId & ~TYPE_MASK) == L3_UNICAST_TYPE) &&
251 ((groupId & TYPE_L3UG_DOUBLE_VLAN_MASK) == TYPE_L3UG_DOUBLE_VLAN_MASK)) {
252 return true;
253 }
254 }
255 }
256 }
257 return false;
258 }
259
260 /**
261 * Configure filtering rules of outer and inner VLAN IDs, and a MAC address.
262 * Filtering happens in three tables (VLAN_TABLE, VLAN_1_TABLE, TMAC_TABLE).
263 *
264 * @param filteringObjective the filtering objective
265 * @param install true to add, false to remove
266 * @param applicationId for application programming this filter
267 */
268 private void processDoubleTaggedFilter(FilteringObjective filteringObjective,
269 boolean install,
270 ApplicationId applicationId) {
271 PortCriterion portCriterion = null;
272 EthCriterion ethCriterion = null;
273 VlanIdCriterion innervidCriterion = null;
274 VlanIdCriterion outerVidCriterion = null;
275 boolean popVlan = false;
276 TrafficTreatment meta = filteringObjective.meta();
277 if (!filteringObjective.key().equals(Criteria.dummy()) &&
278 filteringObjective.key().type() == Criterion.Type.IN_PORT) {
279 portCriterion = (PortCriterion) filteringObjective.key();
280 }
281 if (portCriterion == null) {
282 log.warn("No IN_PORT defined in filtering objective from app: {}" +
283 "Failed to program VLAN tables.", applicationId);
284 return;
285 } else {
286 log.debug("Received filtering objective for dev/port: {}/{}", deviceId,
287 portCriterion.port());
288 }
289
290 // meta should have only one instruction, popVlan.
291 if (meta != null && meta.allInstructions().size() == 1) {
292 L2ModificationInstruction l2Inst = (L2ModificationInstruction) meta.allInstructions().get(0);
293 if (l2Inst.subtype().equals(L2ModificationInstruction.L2SubType.VLAN_POP)) {
294 popVlan = true;
295 } else {
296 log.warn("Filtering objective can have only VLAN_POP instruction.");
297 return;
298 }
299 } else {
300 log.warn("Filtering objective should have one instruction.");
301 return;
302 }
303
304 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
305 for (Criterion criterion : filteringObjective.conditions()) {
306 switch (criterion.type()) {
307 case ETH_DST:
308 case ETH_DST_MASKED:
309 ethCriterion = (EthCriterion) criterion;
310 break;
311 case VLAN_VID:
312 if (innervidCriterion == null) {
313 innervidCriterion = (VlanIdCriterion) criterion;
314 } else {
315 outerVidCriterion = innervidCriterion;
316 innervidCriterion = (VlanIdCriterion) criterion;
317 }
318 break;
319 default:
320 log.warn("Unsupported filter {}", criterion);
321 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
322 return;
323 }
324 }
325
326 if (innervidCriterion == null || outerVidCriterion == null) {
327 log.warn("filtering objective should have two vidCriterion.");
328 return;
329 }
330
331 if (ethCriterion == null || ethCriterion.mac().equals(NONE)) {
332 // NOTE: it is possible that a filtering objective only has vidCriterion
333 log.warn("filtering objective missing dstMac, cannot program TMAC table");
334 return;
335 } else {
336 MacAddress unicastMac = readEthDstFromTreatment(filteringObjective.meta());
337 List<List<FlowRule>> allStages = processEthDstFilter(portCriterion, ethCriterion, innervidCriterion,
338 innervidCriterion.vlanId(), unicastMac,
339 applicationId);
340 for (List<FlowRule> flowRules : allStages) {
341 log.trace("Starting a new flow rule stage for TMAC table flow");
342 ops.newStage();
343
344 for (FlowRule flowRule : flowRules) {
345 log.trace("{} flow rules in TMAC table: {} for dev: {}",
346 (install) ? "adding" : "removing", flowRules, deviceId);
347 if (install) {
348 ops = ops.add(flowRule);
349 } else {
350 // NOTE: Only remove TMAC flow when there is no more enabled port within the
351 // same VLAN on this device if TMAC doesn't support matching on in_port.
352 if (matchInPortTmacTable()
353 || (filteringObjective.meta() != null
354 && filteringObjective.meta().clearedDeferred())) {
355 ops = ops.remove(flowRule);
356 } else {
357 log.debug("Abort TMAC flow removal on {}. Some other ports still share this TMAC flow");
358 }
359 }
360 }
361 }
362 }
363
364 List<FlowRule> rules;
365 rules = processDoubleVlanIdFilter(portCriterion, innervidCriterion,
366 outerVidCriterion, popVlan, applicationId);
367 for (FlowRule flowRule : rules) {
368 log.trace("{} flow rule in VLAN table: {} for dev: {}",
369 (install) ? "adding" : "removing", flowRule, deviceId);
370 ops = install ? ops.add(flowRule) : ops.remove(flowRule);
371 }
372
373 // apply filtering flow rules
374 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
375 @Override
376 public void onSuccess(FlowRuleOperations ops) {
377 log.debug("Applied {} filtering rules in device {}",
378 ops.stages().get(0).size(), deviceId);
379 pass(filteringObjective);
380 }
381
382 @Override
383 public void onError(FlowRuleOperations ops) {
384 log.info("Failed to apply all filtering rules in dev {}", deviceId);
385 fail(filteringObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
386 }
387 }));
388
389 }
390 /**
391 * Internal implementation of processDoubleVlanIdFilter.
392 *
393 * @param portCriterion port on device for which this filter is programmed
394 * @param innerVidCriterion inner vlan
395 * @param outerVidCriterion outer vlan
396 * @param popVlan true if outer vlan header needs to be removed
397 * @param applicationId for application programming this filter
398 * @return flow rules for port-vlan filters
399 */
400 private List<FlowRule> processDoubleVlanIdFilter(PortCriterion portCriterion,
401 VlanIdCriterion innerVidCriterion,
402 VlanIdCriterion outerVidCriterion,
403 boolean popVlan,
404 ApplicationId applicationId) {
405 List<FlowRule> rules = new ArrayList<>();
406 TrafficSelector.Builder outerSelector = DefaultTrafficSelector.builder();
407 TrafficTreatment.Builder outerTreatment = DefaultTrafficTreatment.builder();
408 TrafficSelector.Builder innerSelector = DefaultTrafficSelector.builder();
409 TrafficTreatment.Builder innerTreatment = DefaultTrafficTreatment.builder();
410
411 VlanId outerVlanId = outerVidCriterion.vlanId();
412 VlanId innerVlanId = innerVidCriterion.vlanId();
413 PortNumber portNumber = portCriterion.port();
414 // Check arguments
415 if (PortNumber.ALL.equals(portNumber)
416 || outerVlanId.equals(VlanId.NONE)
417 || innerVlanId.equals(VlanId.NONE)) {
418 log.warn("Incomplete Filtering Objective. " +
419 "VLAN Table cannot be programmed for {}", deviceId);
420 return ImmutableList.of();
421 } else {
422 outerSelector.matchInPort(portNumber);
423 innerSelector.matchInPort(portNumber);
424 outerTreatment.transition(VLAN_1_TABLE);
425 innerTreatment.transition(TMAC_TABLE);
426 outerTreatment.writeMetadata(outerVlanId.toShort(), 0xFFF);
427
428 outerSelector.matchVlanId(outerVlanId);
429 innerSelector.matchVlanId(innerVlanId);
430 //force recompilation
431 //FIXME might be issue due tu /fff mask
432 innerSelector.matchMetadata(outerVlanId.toShort());
433
434 if (popVlan) {
435 outerTreatment.popVlan();
436 }
437 }
438
439 // NOTE: for double-tagged packets, restore original outer vlan
440 // before sending it to the controller.
441 if (supportPuntGroup()) {
442 GroupKey groupKey = popVlanPuntGroupKey();
443 Group group = groupService.getGroup(deviceId, groupKey);
444 if (group != null) {
445 // push outer vlan and send to controller
446 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
447 .matchInPort(portNumber)
448 .matchVlanId(innerVlanId);
449 Host host = handler().get(HostService.class).getConnectedHosts(ConnectPoint.
450 deviceConnectPoint(deviceId + "/" + portNumber.toLong())).stream().filter(h ->
451 h.vlan().equals(outerVlanId)).findFirst().orElse(null);
452 EthType vlanType = EthType.EtherType.VLAN.ethType();
453 if (host != null) {
454 vlanType = host.tpid();
455 }
456 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
457 .pushVlan(vlanType).setVlanId(outerVlanId).punt();
458
459 rules.add(DefaultFlowRule.builder()
460 .forDevice(deviceId)
461 .withSelector(sbuilder.build())
462 .withTreatment(tbuilder.build())
463 .withPriority(PacketPriority.CONTROL.priorityValue())
464 .fromApp(driverId)
465 .makePermanent()
466 .forTable(PUNT_TABLE).build());
467 } else {
468 log.info("popVlanPuntGroup not found in dev:{}", deviceId);
469 return Collections.emptyList();
470 }
471 }
472 FlowRule outerRule = DefaultFlowRule.builder()
473 .forDevice(deviceId)
474 .withSelector(outerSelector.build())
475 .withTreatment(outerTreatment.build())
476 .withPriority(DEFAULT_PRIORITY)
477 .fromApp(applicationId)
478 .makePermanent()
479 .forTable(VLAN_TABLE)
480 .build();
481 FlowRule innerRule = DefaultFlowRule.builder()
482 .forDevice(deviceId)
483 .withSelector(innerSelector.build())
484 .withTreatment(innerTreatment.build())
485 .withPriority(DEFAULT_PRIORITY)
486 .fromApp(applicationId)
487 .makePermanent()
488 .forTable(VLAN_1_TABLE)
489 .build();
490 rules.add(outerRule);
491 rules.add(innerRule);
492
493 return rules;
494 }
495
496 /**
497 * In the OF-DPA 2.0 pipeline, egress forwarding objectives go to the
498 * egress tables.
499 * @param fwd the forwarding objective of type 'egress'
500 * @return a collection of flow rules to be sent to the switch. An empty
501 * collection may be returned if there is a problem in processing
502 * the flow rule
503 */
504 @Override
505 protected Collection<FlowRule> processEgress(ForwardingObjective fwd) {
506 log.debug("Processing egress forwarding objective:{} in dev:{}",
507 fwd, deviceId);
508
509 List<FlowRule> rules = new ArrayList<>();
510
511 // Build selector
512 TrafficSelector.Builder sb = DefaultTrafficSelector.builder();
513 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) fwd.selector().getCriterion(Criterion.Type.VLAN_VID);
514 if (vlanIdCriterion == null) {
515 log.error("Egress forwarding objective:{} must include vlanId", fwd.id());
516 fail(fwd, ObjectiveError.BADPARAMS);
517 return rules;
518 }
519
520 Optional<Instruction> outInstr = fwd.treatment().allInstructions().stream()
521 .filter(instruction -> instruction instanceof Instructions.OutputInstruction).findFirst();
522 if (!outInstr.isPresent()) {
523 log.error("Egress forwarding objective:{} must include output port", fwd.id());
524 fail(fwd, ObjectiveError.BADPARAMS);
525 return rules;
526 }
527
528 PortNumber portNumber = ((Instructions.OutputInstruction) outInstr.get()).port();
529
530 sb.matchVlanId(vlanIdCriterion.vlanId());
531 OfdpaMatchActsetOutput actsetOutput = new OfdpaMatchActsetOutput(portNumber);
532 sb.extension(actsetOutput, deviceId);
533
534 // Build a flow rule for Egress VLAN Flow table
535 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
536 tb.transition(UNICAST_ROUTING_TABLE_1);
537 if (fwd.treatment() != null) {
538 for (Instruction instr : fwd.treatment().allInstructions()) {
539 if (instr instanceof L2ModificationInstruction &&
540 ((L2ModificationInstruction) instr).subtype() ==
541 L2ModificationInstruction.L2SubType.VLAN_ID) {
542 tb.immediate().add(instr);
543 }
544 if (instr instanceof L2ModificationInstruction &&
545 ((L2ModificationInstruction) instr).subtype() ==
546 L2ModificationInstruction.L2SubType.VLAN_PUSH) {
547 EthType ethType = ((L2ModificationInstruction.ModVlanHeaderInstruction) instr).ethernetType();
548 tb.immediate().pushVlan(ethType);
549 }
550 }
551 }
552
553 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
554 .fromApp(fwd.appId())
555 .withPriority(fwd.priority())
556 .forDevice(deviceId)
557 .withSelector(sb.build())
558 .withTreatment(tb.build())
559 .makePermanent()
560 .forTable(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
561 rules.add(ruleBuilder.build());
562 return rules;
563 }
564
565 /**
566 * Handles forwarding rules to the IP Unicast Routing.
567 *
568 * @param fwd the forwarding objective
569 * @return A collection of flow rules, or an empty set
570 */
571 protected Collection<FlowRule> processDoubleTaggedFwd(ForwardingObjective fwd) {
572 // inner for UNICAST_ROUTING_TABLE_1, outer for UNICAST_ROUTING_TABLE
573 TrafficSelector selector = fwd.selector();
574 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
575 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
576 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
577
578 EthTypeCriterion ethType =
579 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
580
581 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
582 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
583 sBuilder.matchVlanId(VlanId.ANY);
584 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
585 if (!ipv4Dst.isMulticast() && ipv4Dst.prefixLength() == 32) {
586 sBuilder.matchIPDst(ipv4Dst);
587 if (fwd.nextId() != null) {
588 NextGroup next = getGroupForNextObjective(fwd.nextId());
589 if (next != null) {
590 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
591 // we only need the top level group's key to point the flow to it
592 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
593 if (group == null) {
594 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
595 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
596 fail(fwd, ObjectiveError.GROUPMISSING);
597 return Collections.emptySet();
598 }
599 outerTtb.immediate().setVlanId(extractDummyVlanIdFromGroupId(group.id().id()));
600 //ACTSET_OUTPUT in OVS will match output action in write_action() set.
601 outerTtb.deferred().setOutput(extractOutputPortFromGroupId(group.id().id()));
602 outerTtb.transition(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
603 innerTtb.deferred().group(group.id());
604 innerTtb.transition(ACL_TABLE);
605
606 FlowRule.Builder innerRuleBuilder = DefaultFlowRule.builder()
607 .fromApp(fwd.appId())
608 .withPriority(fwd.priority())
609 .forDevice(deviceId)
610 .withSelector(sBuilder.build())
611 .withTreatment(innerTtb.build())
612 .forTable(UNICAST_ROUTING_TABLE_1);
613 if (fwd.permanent()) {
614 innerRuleBuilder.makePermanent();
615 } else {
616 innerRuleBuilder.makeTemporary(fwd.timeout());
617 }
618 Collection<FlowRule> flowRuleCollection = new HashSet<>();
619 flowRuleCollection.add(innerRuleBuilder.build());
620
621 FlowRule.Builder outerRuleBuilder = DefaultFlowRule.builder()
622 .fromApp(fwd.appId())
623 .withPriority(fwd.priority())
624 .forDevice(deviceId)
625 .withSelector(sBuilder.build())
626 .withTreatment(outerTtb.build())
627 .forTable(UNICAST_ROUTING_TABLE);
628 if (fwd.permanent()) {
629 outerRuleBuilder.makePermanent();
630 } else {
631 outerRuleBuilder.makeTemporary(fwd.timeout());
632 }
633 flowRuleCollection.add(innerRuleBuilder.build());
634 flowRuleCollection.add(outerRuleBuilder.build());
635 return flowRuleCollection;
636 } else {
637 log.warn("Cannot find group for nextId:{} in dev:{}. Aborting fwd:{}",
638 fwd.nextId(), deviceId, fwd.id());
639 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
640 return Collections.emptySet();
641 }
642 } else {
643 log.warn("NextId is not specified in fwd:{}", fwd.id());
644 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
645 return Collections.emptySet();
646 }
647 }
648 }
649 return Collections.emptySet();
650 }
651
652 private static VlanId extractDummyVlanIdFromGroupId(int groupId) {
653 short vlanId = (short) ((groupId & 0x7FF8000) >> 15);
654 return VlanId.vlanId(vlanId);
655 }
656
657 private static PortNumber extractOutputPortFromGroupId(int groupId) {
658 return PortNumber.portNumber(groupId & 0x7FFF);
659 }
660
Saurav Das4ce45962015-11-24 23:21:05 -0800661 /*
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700662 * Cpqd emulation does not require the non OF-standard rules for
663 * matching untagged packets that ofdpa uses.
Saurav Das4ce45962015-11-24 23:21:05 -0800664 *
665 * (non-Javadoc)
666 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter
667 */
Saurav Das558afec2015-05-31 17:12:48 -0700668 @Override
Charles Chan66291502018-03-02 16:43:28 -0800669 protected List<List<FlowRule>> processVlanIdFilter(PortCriterion portCriterion,
Saurav Das2857f382015-11-03 14:39:27 -0800670 VlanIdCriterion vidCriterion,
671 VlanId assignedVlan,
672 ApplicationId applicationId) {
Charles Chan79769232016-07-05 16:34:39 -0700673 List<FlowRule> rules = new ArrayList<>();
Saurav Das2857f382015-11-03 14:39:27 -0800674 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
675 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
676 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800677 treatment.transition(TMAC_TABLE);
678
Saurav Das2857f382015-11-03 14:39:27 -0800679 if (vidCriterion.vlanId() == VlanId.NONE) {
680 // untagged packets are assigned vlans
681 treatment.pushVlan().setVlanId(assignedVlan);
Piere5bff482018-03-07 11:42:50 +0100682 } else if (!vidCriterion.vlanId().equals(assignedVlan)) {
683 // Rewrite with assigned vlans
684 treatment.setVlanId(assignedVlan);
Saurav Das2857f382015-11-03 14:39:27 -0800685 }
Saurav Das2857f382015-11-03 14:39:27 -0800686
687 // ofdpa cannot match on ALL portnumber, so we need to use separate
688 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800689 List<PortNumber> portnums = new ArrayList<>();
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700690 if (portCriterion != null) {
691 if (portCriterion.port() == PortNumber.ALL) {
692 for (Port port : deviceService.getPorts(deviceId)) {
693 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
694 portnums.add(port.number());
695 }
Saurav Das2857f382015-11-03 14:39:27 -0800696 }
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700697 } else {
698 portnums.add(portCriterion.port());
Saurav Das2857f382015-11-03 14:39:27 -0800699 }
Saurav Das2857f382015-11-03 14:39:27 -0800700 }
Saurav Das4f980082015-11-05 13:39:15 -0800701
Saurav Das2857f382015-11-03 14:39:27 -0800702 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800703 // NOTE: Emulating OFDPA behavior by popping off internal assigned
704 // VLAN before sending to controller
705 if (supportPuntGroup() && vidCriterion.vlanId() == VlanId.NONE) {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700706 try {
707 groupCheckerLock.lock();
708 if (flowRuleQueue == null) {
709 // this means that the group has been created
710 // and that groupChecker has destroyed the queue
711 log.debug("Installing punt table rule for untagged port {} and vlan {}.",
712 pnum, assignedVlan);
713 rules.add(buildPuntTableRule(pnum, assignedVlan));
714 } else {
715 // The VLAN punt group may be held back due to device initial audit.
716 // In that case, we queue all punt table flow until the group has been created.
717 log.debug("popVlanPuntGroup not found in dev:{}, queueing this flow rule.", deviceId);
718 flowRuleQueue.add(buildPuntTableRule(pnum, assignedVlan));
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700719 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700720 } finally {
721 groupCheckerLock.unlock();
Charles Chan0f43e472017-02-14 14:00:16 -0800722 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700723 } else if (vidCriterion.vlanId() != VlanId.NONE) {
724 // for tagged ports just forward to the controller
725 log.debug("Installing punt rule for tagged port {} and vlan {}.", pnum, vidCriterion.vlanId());
726 rules.add(buildPuntTableRuleTagged(pnum, vidCriterion.vlanId()));
Charles Chan0f43e472017-02-14 14:00:16 -0800727 }
728
Saurav Das4f980082015-11-05 13:39:15 -0800729 // create rest of flowrule
Saurav Das2857f382015-11-03 14:39:27 -0800730 selector.matchInPort(pnum);
731 FlowRule rule = DefaultFlowRule.builder()
732 .forDevice(deviceId)
733 .withSelector(selector.build())
734 .withTreatment(treatment.build())
735 .withPriority(DEFAULT_PRIORITY)
736 .fromApp(applicationId)
737 .makePermanent()
738 .forTable(VLAN_TABLE).build();
739 rules.add(rule);
740 }
Charles Chanf57a8252016-06-29 19:12:37 -0700741
Charles Chan66291502018-03-02 16:43:28 -0800742 return ImmutableList.of(rules);
Saurav Das2857f382015-11-03 14:39:27 -0800743 }
744
Pier Ventree0ae7a32016-11-23 09:57:42 -0800745 /**
Charles Chan0f43e472017-02-14 14:00:16 -0800746 * Creates punt table entry that matches IN_PORT and VLAN_VID and points to
747 * a group that pop vlan and punt.
748 *
749 * @param portNumber port number
750 * @param assignedVlan internally assigned vlan id
751 * @return punt table flow rule
752 */
753 private FlowRule buildPuntTableRule(PortNumber portNumber, VlanId assignedVlan) {
754 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
755 .matchInPort(portNumber)
756 .matchVlanId(assignedVlan);
757 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
Yi Tsengfa394de2017-02-01 11:26:40 -0800758 .group(new GroupId(POP_VLAN_PUNT_GROUP_ID));
Charles Chan0f43e472017-02-14 14:00:16 -0800759
760 return DefaultFlowRule.builder()
761 .forDevice(deviceId)
762 .withSelector(sbuilder.build())
763 .withTreatment(tbuilder.build())
764 .withPriority(PacketPriority.CONTROL.priorityValue())
765 .fromApp(driverId)
766 .makePermanent()
767 .forTable(PUNT_TABLE).build();
768 }
769
770 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700771 * Creates punt table entry that matches IN_PORT and VLAN_VID and forwards
772 * packet to controller tagged.
773 *
774 * @param portNumber port number
775 * @param packetVlan vlan tag of the packet
776 * @return punt table flow rule
777 */
778 private FlowRule buildPuntTableRuleTagged(PortNumber portNumber, VlanId packetVlan) {
779 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
780 .matchInPort(portNumber)
781 .matchVlanId(packetVlan);
782 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder().punt();
783
784 return DefaultFlowRule.builder()
785 .forDevice(deviceId)
786 .withSelector(sbuilder.build())
787 .withTreatment(tbuilder.build())
788 .withPriority(PacketPriority.CONTROL.priorityValue())
789 .fromApp(driverId)
790 .makePermanent()
791 .forTable(PUNT_TABLE).build();
792 }
793
794 /**
Pier Ventree0ae7a32016-11-23 09:57:42 -0800795 * Builds a punt to the controller rule for the arp protocol.
Charles Chan0f43e472017-02-14 14:00:16 -0800796 * <p>
797 * NOTE: CpqD cannot punt correctly in group bucket. The current impl will
798 * pop VLAN before sending to controller disregarding whether
799 * it's an internally assigned VLAN or a natural VLAN.
800 * Therefore, trunk port is not supported in CpqD.
Pier Ventree0ae7a32016-11-23 09:57:42 -0800801 *
802 * @param assignedVlan the internal assigned vlan id
803 * @param applicationId the application id
804 * @return the punt flow rule for the arp
805 */
806 private FlowRule buildArpPunt(VlanId assignedVlan, ApplicationId applicationId) {
807 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
808 .matchEthType(Ethernet.TYPE_ARP)
809 .matchVlanId(assignedVlan);
810 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
811 .popVlan()
812 .punt();
813
814 return DefaultFlowRule.builder()
815 .forDevice(deviceId)
816 .withSelector(sbuilder.build())
817 .withTreatment(tbuilder.build())
818 .withPriority(PacketPriority.CONTROL.priorityValue() + 1)
819 .fromApp(applicationId)
820 .makePermanent()
821 .forTable(ACL_TABLE).build();
822 }
823
824 /**
825 * Builds a punt to the controller rule for the icmp v6 messages.
Charles Chan0f43e472017-02-14 14:00:16 -0800826 * <p>
827 * NOTE: CpqD cannot punt correctly in group bucket. The current impl will
828 * pop VLAN before sending to controller disregarding whether
829 * it's an internally assigned VLAN or a natural VLAN.
830 * Therefore, trunk port is not supported in CpqD.
Pier Ventree0ae7a32016-11-23 09:57:42 -0800831 *
832 * @param assignedVlan the internal assigned vlan id
833 * @param applicationId the application id
834 * @return the punt flow rule for the icmp v6 messages
835 */
836 private FlowRule buildIcmpV6Punt(VlanId assignedVlan, ApplicationId applicationId) {
837 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
838 .matchVlanId(assignedVlan)
839 .matchEthType(Ethernet.TYPE_IPV6)
840 .matchIPProtocol(PROTOCOL_ICMP6);
841 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
842 .popVlan()
843 .punt();
844
845 return DefaultFlowRule.builder()
846 .forDevice(deviceId)
847 .withSelector(sbuilder.build())
848 .withTreatment(tbuilder.build())
849 .withPriority(PacketPriority.CONTROL.priorityValue() + 1)
850 .fromApp(applicationId)
851 .makePermanent()
852 .forTable(ACL_TABLE).build();
853 }
854
Saurav Das4ce45962015-11-24 23:21:05 -0800855 /*
856 * Cpqd emulation does not handle vlan tags and mpls labels correctly.
857 * Workaround requires popping off the VLAN tags in the TMAC table.
858 *
859 * (non-Javadoc)
860 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthDstFilter
861 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800862 @Override
Charles Chan66291502018-03-02 16:43:28 -0800863 protected List<List<FlowRule>> processEthDstFilter(PortCriterion portCriterion,
Saurav Das4ce45962015-11-24 23:21:05 -0800864 EthCriterion ethCriterion,
865 VlanIdCriterion vidCriterion,
866 VlanId assignedVlan,
Charles Chan45b69ab2018-03-02 15:41:41 -0800867 MacAddress unicastMac,
Saurav Das4ce45962015-11-24 23:21:05 -0800868 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800869 // Consider PortNumber.ANY as wildcard. Match ETH_DST only
870 if (portCriterion != null && portCriterion.port() == PortNumber.ANY) {
871 return processEthDstOnlyFilter(ethCriterion, applicationId);
872 }
873
Charles Chan5b9df8d2016-03-28 22:21:40 -0700874 // Multicast MAC
875 if (ethCriterion.mask() != null) {
Charles Chan45b69ab2018-03-02 15:41:41 -0800876 return processMcastEthDstFilter(ethCriterion, assignedVlan, unicastMac, applicationId);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700877 }
878
Saurav Das4ce45962015-11-24 23:21:05 -0800879 //handling untagged packets via assigned VLAN
880 if (vidCriterion.vlanId() == VlanId.NONE) {
881 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
882 }
883 // ofdpa cannot match on ALL portnumber, so we need to use separate
884 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800885 List<PortNumber> portnums = new ArrayList<>();
Ray Milkey74e59132018-01-17 15:24:52 -0800886 if (portCriterion != null) {
887 if (portCriterion.port() == PortNumber.ALL) {
888 for (Port port : deviceService.getPorts(deviceId)) {
889 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
890 portnums.add(port.number());
891 }
Saurav Das4ce45962015-11-24 23:21:05 -0800892 }
Ray Milkey74e59132018-01-17 15:24:52 -0800893 } else {
894 portnums.add(portCriterion.port());
Saurav Das4ce45962015-11-24 23:21:05 -0800895 }
Saurav Das4ce45962015-11-24 23:21:05 -0800896 }
897
Charles Chan50d900c2018-03-02 13:26:22 -0800898 List<FlowRule> rules = new ArrayList<>();
Saurav Das4ce45962015-11-24 23:21:05 -0800899 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800900 // TMAC rules for unicast IP packets
Saurav Das4ce45962015-11-24 23:21:05 -0800901 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
902 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
903 selector.matchInPort(pnum);
904 selector.matchVlanId(vidCriterion.vlanId());
905 selector.matchEthType(Ethernet.TYPE_IPV4);
906 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800907 if (!supportTaggedMpls()) {
908 treatment.popVlan();
909 }
Saurav Das4ce45962015-11-24 23:21:05 -0800910 treatment.transition(UNICAST_ROUTING_TABLE);
911 FlowRule rule = DefaultFlowRule.builder()
912 .forDevice(deviceId)
913 .withSelector(selector.build())
914 .withTreatment(treatment.build())
915 .withPriority(DEFAULT_PRIORITY)
916 .fromApp(applicationId)
917 .makePermanent()
918 .forTable(TMAC_TABLE).build();
919 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800920
921 // TMAC rules for MPLS packets
Saurav Das4ce45962015-11-24 23:21:05 -0800922 selector = DefaultTrafficSelector.builder();
923 treatment = DefaultTrafficTreatment.builder();
924 selector.matchInPort(pnum);
925 selector.matchVlanId(vidCriterion.vlanId());
926 selector.matchEthType(Ethernet.MPLS_UNICAST);
927 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800928 if (!supportTaggedMpls()) {
929 treatment.popVlan();
930 }
Saurav Das4ce45962015-11-24 23:21:05 -0800931 treatment.transition(MPLS_TABLE_0);
932 rule = DefaultFlowRule.builder()
933 .forDevice(deviceId)
934 .withSelector(selector.build())
935 .withTreatment(treatment.build())
936 .withPriority(DEFAULT_PRIORITY)
937 .fromApp(applicationId)
938 .makePermanent()
939 .forTable(TMAC_TABLE).build();
940 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800941
942 // TMAC rules for IPv6 packets
Pier Ventree0ae7a32016-11-23 09:57:42 -0800943 selector = DefaultTrafficSelector.builder();
944 treatment = DefaultTrafficTreatment.builder();
945 selector.matchInPort(pnum);
946 selector.matchVlanId(vidCriterion.vlanId());
947 selector.matchEthType(Ethernet.TYPE_IPV6);
948 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800949 if (!supportTaggedMpls()) {
950 treatment.popVlan();
951 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800952 treatment.transition(UNICAST_ROUTING_TABLE);
953 rule = DefaultFlowRule.builder()
954 .forDevice(deviceId)
955 .withSelector(selector.build())
956 .withTreatment(treatment.build())
957 .withPriority(DEFAULT_PRIORITY)
958 .fromApp(applicationId)
959 .makePermanent()
960 .forTable(TMAC_TABLE).build();
961 rules.add(rule);
Saurav Das4ce45962015-11-24 23:21:05 -0800962 }
Charles Chan66291502018-03-02 16:43:28 -0800963 return ImmutableList.of(rules);
Saurav Das4ce45962015-11-24 23:21:05 -0800964 }
965
Charles Chan5270ed02016-01-30 23:22:37 -0800966 @Override
Charles Chan66291502018-03-02 16:43:28 -0800967 protected List<List<FlowRule>> processEthDstOnlyFilter(EthCriterion ethCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700968 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800969 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
970 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
971 selector.matchEthType(Ethernet.TYPE_IPV4);
972 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800973 if (!supportTaggedMpls()) {
974 treatment.popVlan();
975 }
Charles Chan5270ed02016-01-30 23:22:37 -0800976 treatment.transition(UNICAST_ROUTING_TABLE);
977 FlowRule rule = DefaultFlowRule.builder()
978 .forDevice(deviceId)
979 .withSelector(selector.build())
980 .withTreatment(treatment.build())
981 .withPriority(DEFAULT_PRIORITY)
982 .fromApp(applicationId)
983 .makePermanent()
984 .forTable(TMAC_TABLE).build();
Charles Chan66291502018-03-02 16:43:28 -0800985 return ImmutableList.of(ImmutableList.of(rule));
Charles Chan5270ed02016-01-30 23:22:37 -0800986 }
987
Saurav Das4ce45962015-11-24 23:21:05 -0800988 /*
989 * Cpqd emulation allows MPLS ecmp.
990 *
991 * (non-Javadoc)
992 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthTypeSpecific
993 */
994 @Override
995 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
Andrea Campanella7c977b92018-05-30 21:39:45 -0700996 if (isDoubleTagged(fwd)) {
997 return processDoubleTaggedFwd(fwd);
998 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800999 TrafficSelector selector = fwd.selector();
1000 EthTypeCriterion ethType =
1001 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
1002 if ((ethType == null) ||
1003 (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
Pier Ventree0ae7a32016-11-23 09:57:42 -08001004 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST) &&
1005 (ethType.ethType().toShort() != Ethernet.TYPE_IPV6)) {
Saurav Das4ce45962015-11-24 23:21:05 -08001006 log.warn("processSpecific: Unsupported forwarding objective criteria"
1007 + "ethType:{} in dev:{}", ethType, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -08001008 fail(fwd, ObjectiveError.UNSUPPORTED);
1009 return Collections.emptySet();
1010 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001011 boolean defaultRule = false;
Charles Chan50d900c2018-03-02 13:26:22 -08001012 int forTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -08001013 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
Flavio Castroe10fa242016-01-15 12:43:51 -08001014 TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder();
1015
Saurav Das8a0732e2015-11-20 15:27:53 -08001016 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Flavio Castroe10fa242016-01-15 12:43:51 -08001017 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
Charles Chan5b9df8d2016-03-28 22:21:40 -07001018 if (ipv4Dst.isMulticast()) {
1019 if (ipv4Dst.prefixLength() != 32) {
1020 log.warn("Multicast specific forwarding objective can only be /32");
1021 fail(fwd, ObjectiveError.BADPARAMS);
1022 return ImmutableSet.of();
1023 }
1024 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
1025 if (assignedVlan == null) {
1026 log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
1027 fail(fwd, ObjectiveError.BADPARAMS);
1028 return ImmutableSet.of();
1029 }
1030 filteredSelector.matchVlanId(assignedVlan);
1031 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
1032 forTableId = MULTICAST_ROUTING_TABLE;
1033 log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
1034 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -08001035 } else {
Charles Chanf9e98652016-09-07 16:54:23 -07001036 if (ipv4Dst.prefixLength() == 0) {
1037 // The entire IPV4_DST field is wildcarded intentionally
1038 filteredSelector.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001039 } else {
Charles Chanf9e98652016-09-07 16:54:23 -07001040 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
Charles Chan5b9df8d2016-03-28 22:21:40 -07001041 }
1042 forTableId = UNICAST_ROUTING_TABLE;
1043 log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
1044 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -08001045 }
Pier Ventree0ae7a32016-11-23 09:57:42 -08001046 } else if (ethType.ethType().toShort() == Ethernet.TYPE_IPV6) {
Julia Ferguson65428c32017-08-10 18:15:24 +00001047 IpPrefix ipv6Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV6_DST)).ip();
1048 if (ipv6Dst.isMulticast()) {
1049 if (ipv6Dst.prefixLength() != IpAddress.INET6_BIT_LENGTH) {
1050 log.debug("Multicast specific IPv6 forwarding objective can only be /128");
1051 fail(fwd, ObjectiveError.BADPARAMS);
1052 return ImmutableSet.of();
1053 }
1054 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
1055 if (assignedVlan == null) {
1056 log.debug("VLAN ID required by multicast specific fwd obj is missing. Abort.");
1057 fail(fwd, ObjectiveError.BADPARAMS);
1058 return ImmutableSet.of();
1059 }
1060 filteredSelector.matchVlanId(assignedVlan);
1061 filteredSelector.matchEthType(Ethernet.TYPE_IPV6).matchIPv6Dst(ipv6Dst);
1062 forTableId = MULTICAST_ROUTING_TABLE;
1063 log.debug("processing IPv6 multicast specific forwarding objective {} -> next:{}"
1064 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
1065 } else {
1066 if (buildIpv6Selector(filteredSelector, fwd) < 0) {
1067 return Collections.emptyList();
1068 }
1069 forTableId = UNICAST_ROUTING_TABLE;
Pier Ventree0ae7a32016-11-23 09:57:42 -08001070 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001071 } else {
1072 filteredSelector
1073 .matchEthType(Ethernet.MPLS_UNICAST)
1074 .matchMplsLabel(((MplsCriterion)
1075 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
1076 MplsBosCriterion bos = (MplsBosCriterion) selector
1077 .getCriterion(Criterion.Type.MPLS_BOS);
1078 if (bos != null) {
1079 filteredSelector.matchMplsBos(bos.mplsBos());
1080 }
1081 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -08001082 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
1083 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -08001084 }
1085
1086 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
1087 if (fwd.treatment() != null) {
1088 for (Instruction i : fwd.treatment().allInstructions()) {
Charles Chan40132b32017-01-22 00:19:37 -08001089 if (!supportCopyTtl() && i instanceof L3ModificationInstruction) {
1090 L3ModificationInstruction l3instr = (L3ModificationInstruction) i;
1091 if (l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_IN) ||
1092 l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_OUT)) {
1093 continue;
1094 }
1095 }
Charles Chan7d10b162015-12-07 18:54:45 -08001096 /*
1097 * NOTE: OF-DPA does not support immediate instruction in
1098 * L3 unicast and MPLS table.
1099 */
1100 tb.deferred().add(i);
Saurav Das8a0732e2015-11-20 15:27:53 -08001101 }
1102 }
1103
1104 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -08001105 NextGroup next = getGroupForNextObjective(fwd.nextId());
1106 if (next != null) {
1107 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1108 // we only need the top level group's key to point the flow to it
1109 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1110 if (group == null) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001111 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1112 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -08001113 fail(fwd, ObjectiveError.GROUPMISSING);
1114 return Collections.emptySet();
1115 }
1116 tb.deferred().group(group.id());
Saurav Das8a0732e2015-11-20 15:27:53 -08001117 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001118 }
1119 tb.transition(ACL_TABLE);
1120 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
1121 .fromApp(fwd.appId())
1122 .withPriority(fwd.priority())
1123 .forDevice(deviceId)
1124 .withSelector(filteredSelector.build())
1125 .withTreatment(tb.build())
1126 .forTable(forTableId);
1127
1128 if (fwd.permanent()) {
1129 ruleBuilder.makePermanent();
1130 } else {
1131 ruleBuilder.makeTemporary(fwd.timeout());
1132 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001133 Collection<FlowRule> flowRuleCollection = new ArrayList<>();
1134 flowRuleCollection.add(ruleBuilder.build());
1135 if (defaultRule) {
Pier Ventree0ae7a32016-11-23 09:57:42 -08001136 flowRuleCollection.add(
1137 defaultRoute(fwd, complementarySelector, forTableId, tb)
1138 );
Flavio Castroe10fa242016-01-15 12:43:51 -08001139 log.debug("Default rule 0.0.0.0/0 is being installed two rules");
1140 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001141 return flowRuleCollection;
Saurav Das8a0732e2015-11-20 15:27:53 -08001142 }
1143
Charles Chan1e492d32016-01-30 23:22:37 -08001144 @Override
1145 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
1146 List<FlowRule> rules = new ArrayList<>();
1147
1148 // Build filtered selector
1149 TrafficSelector selector = fwd.selector();
1150 EthCriterion ethCriterion = (EthCriterion) selector
1151 .getCriterion(Criterion.Type.ETH_DST);
1152 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
Andrea Campanella7c977b92018-05-30 21:39:45 -07001153 .getCriterion(VLAN_VID);
Charles Chan1e492d32016-01-30 23:22:37 -08001154
1155 if (vlanIdCriterion == null) {
1156 log.warn("Forwarding objective for bridging requires vlan. Not "
1157 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
1158 fail(fwd, ObjectiveError.BADPARAMS);
1159 return Collections.emptySet();
1160 }
1161
1162 TrafficSelector.Builder filteredSelectorBuilder =
1163 DefaultTrafficSelector.builder();
1164 // Do not match MacAddress for subnet broadcast entry
Charles Chand05f54b2017-02-13 11:56:54 -08001165 if (!ethCriterion.mac().equals(NONE) && !ethCriterion.mac().equals(BROADCAST)) {
Charles Chan1e492d32016-01-30 23:22:37 -08001166 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
1167 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
1168 fwd.id(), fwd.nextId(), deviceId);
1169 } else {
1170 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
1171 + "in dev:{} for vlan:{}",
1172 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
1173 }
1174 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
1175 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
1176
1177 if (fwd.treatment() != null) {
1178 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
1179 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
1180 }
1181
1182 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1183 if (fwd.nextId() != null) {
1184 NextGroup next = getGroupForNextObjective(fwd.nextId());
1185 if (next != null) {
1186 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1187 // we only need the top level group's key to point the flow to it
1188 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1189 if (group != null) {
1190 treatmentBuilder.deferred().group(group.id());
1191 } else {
1192 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1193 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
1194 fail(fwd, ObjectiveError.GROUPMISSING);
1195 return Collections.emptySet();
1196 }
1197 }
1198 }
1199 treatmentBuilder.immediate().transition(ACL_TABLE);
1200 TrafficTreatment filteredTreatment = treatmentBuilder.build();
1201
1202 // Build bridging table entries
1203 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
1204 flowRuleBuilder.fromApp(fwd.appId())
1205 .withPriority(fwd.priority())
1206 .forDevice(deviceId)
1207 .withSelector(filteredSelector)
1208 .withTreatment(filteredTreatment)
1209 .forTable(BRIDGING_TABLE);
1210 if (fwd.permanent()) {
1211 flowRuleBuilder.makePermanent();
1212 } else {
1213 flowRuleBuilder.makeTemporary(fwd.timeout());
1214 }
1215 rules.add(flowRuleBuilder.build());
1216 return rules;
1217 }
1218
Saurav Das52025962016-01-28 22:30:01 -08001219 /*
1220 * In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the
1221 * ACL table. Because we pop off vlan tags in TMAC table,
1222 * we need to avoid matching on vlans in the ACL table.
1223 */
1224 @Override
1225 protected Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
1226 log.info("Processing versatile forwarding objective");
1227
Saurav Das52025962016-01-28 22:30:01 -08001228 if (fwd.nextId() == null && fwd.treatment() == null) {
1229 log.error("Forwarding objective {} from {} must contain "
1230 + "nextId or Treatment", fwd.selector(), fwd.appId());
1231 return Collections.emptySet();
1232 }
1233
1234 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
1235 fwd.selector().criteria().forEach(criterion -> {
1236 if (criterion instanceof VlanIdCriterion) {
1237 // avoid matching on vlans
1238 return;
Pier Ventree0ae7a32016-11-23 09:57:42 -08001239 } else if (criterion instanceof Icmpv6TypeCriterion ||
1240 criterion instanceof Icmpv6CodeCriterion) {
1241 /*
1242 * We silenty discard these criterions, our current
1243 * OFDPA platform does not support these matches on
1244 * the ACL table.
1245 */
1246 log.warn("ICMPv6 Type and ICMPv6 Code are not supported");
Saurav Das52025962016-01-28 22:30:01 -08001247 } else {
1248 sbuilder.add(criterion);
1249 }
1250 });
1251
1252 // XXX driver does not currently do type checking as per Tables 65-67 in
1253 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
1254 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
1255 if (fwd.treatment() != null) {
1256 for (Instruction ins : fwd.treatment().allInstructions()) {
1257 if (ins instanceof OutputInstruction) {
1258 OutputInstruction o = (OutputInstruction) ins;
1259 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan0f43e472017-02-14 14:00:16 -08001260 ttBuilder.transition(PUNT_TABLE);
Saurav Das52025962016-01-28 22:30:01 -08001261 } else {
1262 log.warn("Only allowed treatments in versatile forwarding "
1263 + "objectives are punts to the controller");
1264 }
Charles Chanf76de302018-06-15 18:54:18 -07001265 } else if (ins instanceof NoActionInstruction) {
1266 // No action is allowed and nothing needs to be done
Saurav Das52025962016-01-28 22:30:01 -08001267 } else {
1268 log.warn("Cannot process instruction in versatile fwd {}", ins);
1269 }
1270 }
Charles Chan2df0e8a2017-01-09 11:45:08 -08001271 if (fwd.treatment().clearedDeferred()) {
1272 ttBuilder.wipeDeferred();
1273 }
Saurav Das52025962016-01-28 22:30:01 -08001274 }
1275 if (fwd.nextId() != null) {
1276 // overide case
1277 NextGroup next = getGroupForNextObjective(fwd.nextId());
1278 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1279 // we only need the top level group's key to point the flow to it
1280 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1281 if (group == null) {
1282 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1283 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
1284 fail(fwd, ObjectiveError.GROUPMISSING);
1285 return Collections.emptySet();
1286 }
1287 ttBuilder.deferred().group(group.id());
1288 }
1289
1290 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
1291 .fromApp(fwd.appId())
1292 .withPriority(fwd.priority())
1293 .forDevice(deviceId)
1294 .withSelector(sbuilder.build())
1295 .withTreatment(ttBuilder.build())
1296 .makePermanent()
1297 .forTable(ACL_TABLE);
1298 return Collections.singletonList(ruleBuilder.build());
1299 }
1300
1301 /*
1302 * Cpqd emulation requires table-miss-entries in forwarding tables.
1303 * Real OFDPA does not require these rules as they are put in by default.
1304 *
1305 * (non-Javadoc)
1306 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#initializePipeline()
1307 */
Saurav Das2857f382015-11-03 14:39:27 -08001308 @Override
Saurav Das558afec2015-05-31 17:12:48 -07001309 protected void initializePipeline() {
Charles Chanf6ec1532017-02-08 16:10:40 -08001310 initTableMiss(PORT_TABLE, VLAN_TABLE, null);
1311 initTableMiss(VLAN_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001312 initTableMiss(VLAN_1_TABLE, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001313 initTableMiss(TMAC_TABLE, BRIDGING_TABLE, null);
1314 initTableMiss(UNICAST_ROUTING_TABLE, ACL_TABLE, null);
1315 initTableMiss(MULTICAST_ROUTING_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001316 initTableMiss(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS, ACL_TABLE, null);
1317 initTableMiss(UNICAST_ROUTING_TABLE_1, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001318 initTableMiss(MPLS_TABLE_0, MPLS_TABLE_1, null);
1319 initTableMiss(MPLS_TABLE_1, ACL_TABLE, null);
1320 initTableMiss(BRIDGING_TABLE, ACL_TABLE, null);
1321 initTableMiss(ACL_TABLE, -1, null);
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001322 linkDiscoveryPuntTableRules();
Charles Chan0f43e472017-02-14 14:00:16 -08001323
1324 if (supportPuntGroup()) {
Charles Chan0f43e472017-02-14 14:00:16 -08001325 initPopVlanPuntGroup();
1326 } else {
1327 initTableMiss(PUNT_TABLE, -1,
1328 DefaultTrafficTreatment.builder().popVlan().punt().build());
1329 }
Saurav Das558afec2015-05-31 17:12:48 -07001330 }
1331
Charles Chanf6ec1532017-02-08 16:10:40 -08001332 /**
1333 * Install table-miss flow entry.
1334 *
1335 * If treatment exists, use it directly.
1336 * Else if treatment does not exist but nextTable > 0, transit to next table.
1337 * Else apply empty treatment.
1338 *
1339 * @param thisTable this table ID
1340 * @param nextTable next table ID
1341 * @param treatment traffic treatment to apply.
1342 */
1343 private void initTableMiss(int thisTable, int nextTable, TrafficTreatment treatment) {
Saurav Das558afec2015-05-31 17:12:48 -07001344 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chanf6ec1532017-02-08 16:10:40 -08001345 TrafficSelector selector = DefaultTrafficSelector.builder().build();
Saurav Das558afec2015-05-31 17:12:48 -07001346
Charles Chanf6ec1532017-02-08 16:10:40 -08001347 if (treatment == null) {
1348 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1349 if (nextTable > 0) {
1350 tBuilder.transition(nextTable);
Saurav Das558afec2015-05-31 17:12:48 -07001351 }
Charles Chanf6ec1532017-02-08 16:10:40 -08001352 treatment = tBuilder.build();
1353 }
Saurav Das558afec2015-05-31 17:12:48 -07001354
Charles Chanb7504392017-02-10 12:51:04 -08001355 FlowRule rule = DefaultFlowRule.builder()
1356 .forDevice(deviceId)
Charles Chanf6ec1532017-02-08 16:10:40 -08001357 .withSelector(selector)
1358 .withTreatment(treatment)
Charles Chanb7504392017-02-10 12:51:04 -08001359 .withPriority(LOWEST_PRIORITY)
1360 .fromApp(driverId)
1361 .makePermanent()
Charles Chanf6ec1532017-02-08 16:10:40 -08001362 .forTable(thisTable).build();
Charles Chanb7504392017-02-10 12:51:04 -08001363 ops = ops.add(rule);
Saurav Das2857f382015-11-03 14:39:27 -08001364
1365 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1366 @Override
1367 public void onSuccess(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001368 log.info("Initialized table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001369 }
Saurav Das2857f382015-11-03 14:39:27 -08001370 @Override
1371 public void onError(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001372 log.warn("Failed to initialize table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001373 }
1374 }));
1375 }
Charles Chan0f43e472017-02-14 14:00:16 -08001376
1377 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001378 * Install lldp/bbdp matching rules at table PUNT_TABLE
1379 * that forward traffic to controller.
1380 *
1381 */
1382 private void linkDiscoveryPuntTableRules() {
1383 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1384 TrafficTreatment treatment = DefaultTrafficTreatment.builder().punt().build();
1385
1386 TrafficSelector.Builder lldpSelector = DefaultTrafficSelector.builder();
1387 lldpSelector.matchEthType(EthType.EtherType.LLDP.ethType().toShort());
1388 FlowRule lldpRule = DefaultFlowRule.builder()
1389 .forDevice(deviceId)
1390 .withSelector(lldpSelector.build())
1391 .withTreatment(treatment)
1392 .withPriority(HIGHEST_PRIORITY)
1393 .fromApp(driverId)
1394 .makePermanent()
1395 .forTable(PUNT_TABLE).build();
1396 ops = ops.add(lldpRule);
1397
1398 TrafficSelector.Builder bbdpSelector = DefaultTrafficSelector.builder();
1399 bbdpSelector.matchEthType(EthType.EtherType.BDDP.ethType().toShort());
1400 FlowRule bbdpRule = DefaultFlowRule.builder()
1401 .forDevice(deviceId)
1402 .withSelector(bbdpSelector.build())
1403 .withTreatment(treatment)
1404 .withPriority(HIGHEST_PRIORITY)
1405 .fromApp(driverId)
1406 .makePermanent()
1407 .forTable(PUNT_TABLE).build();
1408 ops.add(bbdpRule);
1409
1410 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1411 @Override
1412 public void onSuccess(FlowRuleOperations ops) {
1413 log.info("Added lldp/bbdp rules for table {} on {}", PUNT_TABLE, deviceId);
1414 }
1415 @Override
1416 public void onError(FlowRuleOperations ops) {
1417 log.warn("Failed to initialize lldp/bbdp rules for table {} on {}", PUNT_TABLE, deviceId);
1418 }
1419 }));
1420 }
1421
1422 /**
Charles Chan0f43e472017-02-14 14:00:16 -08001423 * Builds a indirect group contains pop_vlan and punt actions.
1424 * <p>
1425 * Using group instead of immediate action to ensure that
1426 * the copy of packet on the data plane is not affected by the pop vlan action.
1427 */
1428 private void initPopVlanPuntGroup() {
Yi Tsengef19de12017-04-24 11:33:05 -07001429 GroupKey groupKey = popVlanPuntGroupKey();
Charles Chan0f43e472017-02-14 14:00:16 -08001430 TrafficTreatment bucketTreatment = DefaultTrafficTreatment.builder()
1431 .popVlan().punt().build();
1432 GroupBucket bucket =
1433 DefaultGroupBucket.createIndirectGroupBucket(bucketTreatment);
1434 GroupDescription groupDesc =
1435 new DefaultGroupDescription(
1436 deviceId,
1437 GroupDescription.Type.INDIRECT,
1438 new GroupBuckets(Collections.singletonList(bucket)),
1439 groupKey,
1440 POP_VLAN_PUNT_GROUP_ID,
1441 driverId);
1442 groupService.addGroup(groupDesc);
1443
1444 log.info("Initialized pop vlan punt group on {}", deviceId);
1445 }
Yi Tsengef19de12017-04-24 11:33:05 -07001446
1447 /**
1448 * Generates group key for a static indirect group that pop vlan and punt to
1449 * controller.
1450 *
1451 * @return the group key of the indirect table
1452 */
1453 private GroupKey popVlanPuntGroupKey() {
1454 int hash = POP_VLAN_PUNT_GROUP_ID | (Objects.hash(deviceId) & FOUR_BIT_MASK);
1455 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
1456 }
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001457
1458 private class PopVlanPuntGroupChecker implements Runnable {
1459 @Override
1460 public void run() {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001461 try {
1462 groupCheckerLock.lock();
1463 // this can happen outside of the lock but I think it is safer
1464 // to include it here.
1465 Group group = groupService.getGroup(deviceId, popVlanPuntGroupKey());
1466 if (group != null) {
1467 log.debug("PopVlanPuntGroupChecker: Installing {} missing rules at punt table.",
1468 flowRuleQueue.size());
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001469
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001470 // if we have pending flow rules install them
1471 if (flowRuleQueue.size() > 0) {
1472 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1473 // we should not care about the context here, it can only be add
1474 // since when removing the rules the group should be there already.
1475 flowRuleQueue.forEach(ops::add);
1476 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1477 @Override
1478 public void onSuccess(FlowRuleOperations ops) {
1479 log.debug("Applied {} pop vlan punt rules in device {}",
1480 ops.stages().get(0).size(), deviceId);
1481 }
1482
1483 @Override
1484 public void onError(FlowRuleOperations ops) {
1485 log.error("Failed to apply all pop vlan punt rules in dev {}", deviceId);
1486 }
1487 }));
1488 }
1489 // this signifies that the group is created and now
1490 // flow rules can be installed directly
1491 flowRuleQueue = null;
1492 // shutdown the group checker gracefully
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001493 groupChecker.shutdown();
1494 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001495 } finally {
1496 groupCheckerLock.unlock();
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001497 }
1498 }
1499 }
Saurav Das558afec2015-05-31 17:12:48 -07001500}