blob: 8ca774a8ac39620e0fc8738c41cea7c364e21a17 [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;
49import org.onosproject.net.flow.criteria.MplsBosCriterion;
50import org.onosproject.net.flow.criteria.MplsCriterion;
Saurav Das2857f382015-11-03 14:39:27 -080051import org.onosproject.net.flow.criteria.PortCriterion;
52import org.onosproject.net.flow.criteria.VlanIdCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080053import org.onosproject.net.flow.instructions.Instruction;
Andrea Campanella7c977b92018-05-30 21:39:45 -070054import org.onosproject.net.flow.instructions.Instructions;
Saurav Das52025962016-01-28 22:30:01 -080055import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Charles Chanf76de302018-06-15 18:54:18 -070056import org.onosproject.net.flow.instructions.Instructions.NoActionInstruction;
Andrea Campanella7c977b92018-05-30 21:39:45 -070057import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Charles Chan40132b32017-01-22 00:19:37 -080058import org.onosproject.net.flow.instructions.L3ModificationInstruction;
Andrea Campanella7c977b92018-05-30 21:39:45 -070059import org.onosproject.net.flowobjective.FilteringObjective;
Saurav Das8a0732e2015-11-20 15:27:53 -080060import org.onosproject.net.flowobjective.ForwardingObjective;
61import org.onosproject.net.flowobjective.ObjectiveError;
Charles Chan0f43e472017-02-14 14:00:16 -080062import org.onosproject.net.group.DefaultGroupBucket;
63import org.onosproject.net.group.DefaultGroupDescription;
64import org.onosproject.net.group.DefaultGroupKey;
Saurav Das8a0732e2015-11-20 15:27:53 -080065import org.onosproject.net.group.Group;
Charles Chan0f43e472017-02-14 14:00:16 -080066import org.onosproject.net.group.GroupBucket;
67import org.onosproject.net.group.GroupBuckets;
68import org.onosproject.net.group.GroupDescription;
Saurav Das8a0732e2015-11-20 15:27:53 -080069import org.onosproject.net.group.GroupKey;
Andrea Campanella7c977b92018-05-30 21:39:45 -070070import org.onosproject.net.host.HostService;
Charles Chanf57a8252016-06-29 19:12:37 -070071import org.onosproject.net.packet.PacketPriority;
Saurav Das558afec2015-05-31 17:12:48 -070072import org.slf4j.Logger;
73
Jonathan Hart855179c2016-04-26 07:40:04 -070074import java.util.ArrayList;
75import java.util.Collection;
76import java.util.Collections;
77import java.util.Deque;
Andrea Campanella7c977b92018-05-30 21:39:45 -070078import java.util.HashSet;
Jonathan Hart855179c2016-04-26 07:40:04 -070079import java.util.List;
Charles Chan0f43e472017-02-14 14:00:16 -080080import java.util.Objects;
Andreas Pantelopoulos89369462018-05-30 14:30:24 -070081import java.util.Queue;
82import java.util.concurrent.ConcurrentLinkedQueue;
83import java.util.concurrent.Executors;
84import java.util.concurrent.ScheduledExecutorService;
85import java.util.concurrent.TimeUnit;
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -070086import java.util.concurrent.locks.ReentrantLock;
Andrea Campanella7c977b92018-05-30 21:39:45 -070087import java.util.Optional;
Jonathan Hart855179c2016-04-26 07:40:04 -070088
Charles Chand05f54b2017-02-13 11:56:54 -080089import static org.onlab.packet.MacAddress.BROADCAST;
90import static org.onlab.packet.MacAddress.NONE;
Andreas Pantelopoulos89369462018-05-30 14:30:24 -070091import static org.onlab.util.Tools.groupedThreads;
Yi Tsengef19de12017-04-24 11:33:05 -070092import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*;
pier9469f3e2019-04-17 17:05:08 +020093import static org.onosproject.driver.pipeline.ofdpa.OfdpaPipelineUtility.*;
Andrea Campanella7c977b92018-05-30 21:39:45 -070094import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
Jonathan Hart855179c2016-04-26 07:40:04 -070095import static org.slf4j.LoggerFactory.getLogger;
96
Saurav Das558afec2015-05-31 17:12:48 -070097/**
Charles Chanab591602019-01-22 17:25:04 -080098 * Driver for Open vSwitch emulation of the OFDPA pipeline.
Saurav Das558afec2015-05-31 17:12:48 -070099 */
Charles Chanab591602019-01-22 17:25:04 -0800100public class OvsOfdpaPipeline extends Ofdpa2Pipeline {
Saurav Das558afec2015-05-31 17:12:48 -0700101
102 private final Logger log = getLogger(getClass());
103
Andrea Campanella7c977b92018-05-30 21:39:45 -0700104 private static final int EGRESS_VLAN_FLOW_TABLE_IN_INGRESS = 31;
105 private static final int UNICAST_ROUTING_TABLE_1 = 32;
Charles Chan40132b32017-01-22 00:19:37 -0800106 /**
Charles Chan0f43e472017-02-14 14:00:16 -0800107 * Table that determines whether VLAN is popped before punting to controller.
108 * <p>
109 * This is a non-OFDPA table to emulate OFDPA packet in behavior.
110 * VLAN will be popped before punting if the VLAN is internally assigned.
Charles Chan0f43e472017-02-14 14:00:16 -0800111 */
112 private static final int PUNT_TABLE = 63;
113
114 /**
115 * A static indirect group that pop vlan and punt to controller.
116 * <p>
117 * The purpose of using a group instead of immediate action is that this
118 * won't affect another copy on the data plane when write action exists.
119 */
Charles Chan07372342018-08-14 18:31:17 -0700120 private static final int POP_VLAN_PUNT_GROUP_ID = 0xd0000000;
Charles Chan0f43e472017-02-14 14:00:16 -0800121
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700122 /**
123 * Executor for group checker thread that checks pop vlan punt group.
124 */
125 private ScheduledExecutorService groupChecker;
126
127 /**
128 * Queue for passing pop vlan punt group flow rules to the GroupChecker thread.
129 */
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700130 private Queue<FlowRule> flowRuleQueue;
131
132 /**
133 * Lock used in synchronizing driver thread with groupCheckerThread.
134 */
135 private ReentrantLock groupCheckerLock;
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700136
Charles Chan053b1cb2017-03-22 16:56:35 -0700137 @Override
138 protected boolean requireVlanExtensions() {
139 return false;
140 }
141
Charles Chan425854b2016-04-11 15:32:12 -0700142 @Override
Harshada Chaundkarcb787512019-08-05 15:33:30 +0000143 protected boolean requireEthType() {
144 return false;
145 }
146
147 @Override
Charles Chan40132b32017-01-22 00:19:37 -0800148 protected void initDriverId() {
Charles Chan425854b2016-04-11 15:32:12 -0700149 driverId = coreService.registerApplication(
Charles Chanab591602019-01-22 17:25:04 -0800150 "org.onosproject.driver.OvsOfdpaPipeline");
Charles Chan40132b32017-01-22 00:19:37 -0800151 }
Charles Chan425854b2016-04-11 15:32:12 -0700152
Charles Chan40132b32017-01-22 00:19:37 -0800153 @Override
154 protected void initGroupHander(PipelinerContext context) {
pier836d7fe2020-03-20 11:00:38 +0100155 // Terminate internal references
156 // We are terminating the references here
157 // because when the device is offline the apps
158 // are still sending flowobjectives
159 if (groupHandler != null) {
160 groupHandler.terminate();
161 }
Charles Chanab591602019-01-22 17:25:04 -0800162 groupHandler = new OvsOfdpaGroupHandler();
Charles Chan40132b32017-01-22 00:19:37 -0800163 groupHandler.init(deviceId, context);
Charles Chan425854b2016-04-11 15:32:12 -0700164 }
165
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700166 @Override
167 public void init(DeviceId deviceId, PipelinerContext context) {
pierf37ce522020-03-20 11:00:38 +0100168 synchronized (this) {
169 if (isReady()) {
170 return;
171 }
172
173 // Terminate internal references
174 // We are terminating the references here
175 // because when the device is offline the apps
176 // are still sending flowobjectives
177 if (groupChecker != null) {
178 groupChecker.shutdown();
179 }
pier1b7dd122020-02-28 09:24:11 +0100180 // create a new executor at each init and a new empty queue
181 groupChecker = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/driver",
182 "ovs-ofdpa-%d", log));
pierf37ce522020-03-20 11:00:38 +0100183 if (flowRuleQueue != null) {
184 flowRuleQueue.clear();
185 }
pier1b7dd122020-02-28 09:24:11 +0100186 flowRuleQueue = new ConcurrentLinkedQueue<>();
187 groupCheckerLock = new ReentrantLock();
188 groupChecker.scheduleAtFixedRate(new PopVlanPuntGroupChecker(), 20, 50, TimeUnit.MILLISECONDS);
189 super.init(deviceId, context);
190 }
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700191 }
pier9469f3e2019-04-17 17:05:08 +0200192
Andrea Campanella7c977b92018-05-30 21:39:45 -0700193 protected void processFilter(FilteringObjective filteringObjective,
194 boolean install,
195 ApplicationId applicationId) {
pier9469f3e2019-04-17 17:05:08 +0200196 if (OfdpaPipelineUtility.isDoubleTagged(filteringObjective)) {
Andrea Campanella7c977b92018-05-30 21:39:45 -0700197 processDoubleTaggedFilter(filteringObjective, install, applicationId);
198 } else {
199 // If it is not a double-tagged filter, we fall back
200 // to the OFDPA 2.0 pipeline.
201 super.processFilter(filteringObjective, install, applicationId);
202 }
203 }
204
205 /**
Andrea Campanella7c977b92018-05-30 21:39:45 -0700206 * Determines if the forwarding objective will be used for double-tagged packets.
207 *
208 * @param fwd Forwarding objective
209 * @return True if the objective was created for double-tagged packets, false otherwise.
210 */
211 private boolean isDoubleTagged(ForwardingObjective fwd) {
212 if (fwd.nextId() != null) {
213 NextGroup next = getGroupForNextObjective(fwd.nextId());
214 if (next != null) {
215 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
216 // we only need the top level group's key
217 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
218 if (group != null) {
219 int groupId = group.id().id();
220 if (((groupId & ~TYPE_MASK) == L3_UNICAST_TYPE) &&
221 ((groupId & TYPE_L3UG_DOUBLE_VLAN_MASK) == TYPE_L3UG_DOUBLE_VLAN_MASK)) {
222 return true;
223 }
224 }
225 }
226 }
227 return false;
228 }
229
230 /**
231 * Configure filtering rules of outer and inner VLAN IDs, and a MAC address.
232 * Filtering happens in three tables (VLAN_TABLE, VLAN_1_TABLE, TMAC_TABLE).
233 *
234 * @param filteringObjective the filtering objective
235 * @param install true to add, false to remove
236 * @param applicationId for application programming this filter
237 */
238 private void processDoubleTaggedFilter(FilteringObjective filteringObjective,
239 boolean install,
240 ApplicationId applicationId) {
241 PortCriterion portCriterion = null;
242 EthCriterion ethCriterion = null;
243 VlanIdCriterion innervidCriterion = null;
244 VlanIdCriterion outerVidCriterion = null;
245 boolean popVlan = false;
246 TrafficTreatment meta = filteringObjective.meta();
247 if (!filteringObjective.key().equals(Criteria.dummy()) &&
248 filteringObjective.key().type() == Criterion.Type.IN_PORT) {
249 portCriterion = (PortCriterion) filteringObjective.key();
250 }
251 if (portCriterion == null) {
252 log.warn("No IN_PORT defined in filtering objective from app: {}" +
253 "Failed to program VLAN tables.", applicationId);
254 return;
255 } else {
256 log.debug("Received filtering objective for dev/port: {}/{}", deviceId,
257 portCriterion.port());
258 }
259
260 // meta should have only one instruction, popVlan.
261 if (meta != null && meta.allInstructions().size() == 1) {
262 L2ModificationInstruction l2Inst = (L2ModificationInstruction) meta.allInstructions().get(0);
263 if (l2Inst.subtype().equals(L2ModificationInstruction.L2SubType.VLAN_POP)) {
264 popVlan = true;
265 } else {
266 log.warn("Filtering objective can have only VLAN_POP instruction.");
267 return;
268 }
269 } else {
270 log.warn("Filtering objective should have one instruction.");
271 return;
272 }
273
274 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
275 for (Criterion criterion : filteringObjective.conditions()) {
276 switch (criterion.type()) {
277 case ETH_DST:
278 case ETH_DST_MASKED:
279 ethCriterion = (EthCriterion) criterion;
280 break;
281 case VLAN_VID:
Daniele Morofa382c22019-07-12 17:58:54 -0700282 outerVidCriterion = (VlanIdCriterion) criterion;
283 break;
284 case INNER_VLAN_VID:
285 innervidCriterion = (VlanIdCriterion) criterion;
Andrea Campanella7c977b92018-05-30 21:39:45 -0700286 break;
287 default:
288 log.warn("Unsupported filter {}", criterion);
289 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
290 return;
291 }
292 }
293
294 if (innervidCriterion == null || outerVidCriterion == null) {
295 log.warn("filtering objective should have two vidCriterion.");
296 return;
297 }
298
299 if (ethCriterion == null || ethCriterion.mac().equals(NONE)) {
300 // NOTE: it is possible that a filtering objective only has vidCriterion
301 log.warn("filtering objective missing dstMac, cannot program TMAC table");
302 return;
303 } else {
304 MacAddress unicastMac = readEthDstFromTreatment(filteringObjective.meta());
305 List<List<FlowRule>> allStages = processEthDstFilter(portCriterion, ethCriterion, innervidCriterion,
306 innervidCriterion.vlanId(), unicastMac,
307 applicationId);
308 for (List<FlowRule> flowRules : allStages) {
309 log.trace("Starting a new flow rule stage for TMAC table flow");
310 ops.newStage();
311
312 for (FlowRule flowRule : flowRules) {
313 log.trace("{} flow rules in TMAC table: {} for dev: {}",
314 (install) ? "adding" : "removing", flowRules, deviceId);
315 if (install) {
316 ops = ops.add(flowRule);
317 } else {
318 // NOTE: Only remove TMAC flow when there is no more enabled port within the
319 // same VLAN on this device if TMAC doesn't support matching on in_port.
320 if (matchInPortTmacTable()
321 || (filteringObjective.meta() != null
322 && filteringObjective.meta().clearedDeferred())) {
323 ops = ops.remove(flowRule);
324 } else {
325 log.debug("Abort TMAC flow removal on {}. Some other ports still share this TMAC flow");
326 }
327 }
328 }
329 }
330 }
331
332 List<FlowRule> rules;
333 rules = processDoubleVlanIdFilter(portCriterion, innervidCriterion,
334 outerVidCriterion, popVlan, applicationId);
335 for (FlowRule flowRule : rules) {
336 log.trace("{} flow rule in VLAN table: {} for dev: {}",
337 (install) ? "adding" : "removing", flowRule, deviceId);
338 ops = install ? ops.add(flowRule) : ops.remove(flowRule);
339 }
340
341 // apply filtering flow rules
342 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
343 @Override
344 public void onSuccess(FlowRuleOperations ops) {
345 log.debug("Applied {} filtering rules in device {}",
346 ops.stages().get(0).size(), deviceId);
347 pass(filteringObjective);
348 }
349
350 @Override
351 public void onError(FlowRuleOperations ops) {
352 log.info("Failed to apply all filtering rules in dev {}", deviceId);
353 fail(filteringObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
354 }
355 }));
356
357 }
358 /**
359 * Internal implementation of processDoubleVlanIdFilter.
360 *
361 * @param portCriterion port on device for which this filter is programmed
362 * @param innerVidCriterion inner vlan
363 * @param outerVidCriterion outer vlan
364 * @param popVlan true if outer vlan header needs to be removed
365 * @param applicationId for application programming this filter
366 * @return flow rules for port-vlan filters
367 */
368 private List<FlowRule> processDoubleVlanIdFilter(PortCriterion portCriterion,
369 VlanIdCriterion innerVidCriterion,
370 VlanIdCriterion outerVidCriterion,
371 boolean popVlan,
372 ApplicationId applicationId) {
373 List<FlowRule> rules = new ArrayList<>();
374 TrafficSelector.Builder outerSelector = DefaultTrafficSelector.builder();
375 TrafficTreatment.Builder outerTreatment = DefaultTrafficTreatment.builder();
376 TrafficSelector.Builder innerSelector = DefaultTrafficSelector.builder();
377 TrafficTreatment.Builder innerTreatment = DefaultTrafficTreatment.builder();
378
379 VlanId outerVlanId = outerVidCriterion.vlanId();
380 VlanId innerVlanId = innerVidCriterion.vlanId();
381 PortNumber portNumber = portCriterion.port();
382 // Check arguments
383 if (PortNumber.ALL.equals(portNumber)
384 || outerVlanId.equals(VlanId.NONE)
385 || innerVlanId.equals(VlanId.NONE)) {
386 log.warn("Incomplete Filtering Objective. " +
387 "VLAN Table cannot be programmed for {}", deviceId);
388 return ImmutableList.of();
389 } else {
390 outerSelector.matchInPort(portNumber);
391 innerSelector.matchInPort(portNumber);
392 outerTreatment.transition(VLAN_1_TABLE);
393 innerTreatment.transition(TMAC_TABLE);
394 outerTreatment.writeMetadata(outerVlanId.toShort(), 0xFFF);
395
396 outerSelector.matchVlanId(outerVlanId);
397 innerSelector.matchVlanId(innerVlanId);
398 //force recompilation
399 //FIXME might be issue due tu /fff mask
400 innerSelector.matchMetadata(outerVlanId.toShort());
401
402 if (popVlan) {
403 outerTreatment.popVlan();
404 }
405 }
406
407 // NOTE: for double-tagged packets, restore original outer vlan
408 // before sending it to the controller.
Charles Chanab591602019-01-22 17:25:04 -0800409 GroupKey groupKey = popVlanPuntGroupKey();
410 Group group = groupService.getGroup(deviceId, groupKey);
411 if (group != null) {
412 // push outer vlan and send to controller
413 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
414 .matchInPort(portNumber)
415 .matchVlanId(innerVlanId);
416 Host host = handler().get(HostService.class).getConnectedHosts(ConnectPoint.
417 deviceConnectPoint(deviceId + "/" + portNumber.toLong())).stream().filter(h ->
418 h.vlan().equals(outerVlanId)).findFirst().orElse(null);
419 EthType vlanType = EthType.EtherType.VLAN.ethType();
420 if (host != null) {
421 vlanType = host.tpid();
Andrea Campanella7c977b92018-05-30 21:39:45 -0700422 }
Charles Chanab591602019-01-22 17:25:04 -0800423 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
424 .pushVlan(vlanType).setVlanId(outerVlanId).punt();
425
426 rules.add(DefaultFlowRule.builder()
427 .forDevice(deviceId)
428 .withSelector(sbuilder.build())
429 .withTreatment(tbuilder.build())
430 .withPriority(PacketPriority.CONTROL.priorityValue())
431 .fromApp(driverId)
432 .makePermanent()
433 .forTable(PUNT_TABLE).build());
434 } else {
435 log.info("popVlanPuntGroup not found in dev:{}", deviceId);
436 return Collections.emptyList();
Andrea Campanella7c977b92018-05-30 21:39:45 -0700437 }
Charles Chanab591602019-01-22 17:25:04 -0800438
Andrea Campanella7c977b92018-05-30 21:39:45 -0700439 FlowRule outerRule = DefaultFlowRule.builder()
440 .forDevice(deviceId)
441 .withSelector(outerSelector.build())
442 .withTreatment(outerTreatment.build())
443 .withPriority(DEFAULT_PRIORITY)
444 .fromApp(applicationId)
445 .makePermanent()
446 .forTable(VLAN_TABLE)
447 .build();
448 FlowRule innerRule = DefaultFlowRule.builder()
449 .forDevice(deviceId)
450 .withSelector(innerSelector.build())
451 .withTreatment(innerTreatment.build())
452 .withPriority(DEFAULT_PRIORITY)
453 .fromApp(applicationId)
454 .makePermanent()
455 .forTable(VLAN_1_TABLE)
456 .build();
457 rules.add(outerRule);
458 rules.add(innerRule);
459
460 return rules;
461 }
462
463 /**
464 * In the OF-DPA 2.0 pipeline, egress forwarding objectives go to the
465 * egress tables.
466 * @param fwd the forwarding objective of type 'egress'
467 * @return a collection of flow rules to be sent to the switch. An empty
468 * collection may be returned if there is a problem in processing
469 * the flow rule
470 */
471 @Override
472 protected Collection<FlowRule> processEgress(ForwardingObjective fwd) {
473 log.debug("Processing egress forwarding objective:{} in dev:{}",
474 fwd, deviceId);
475
476 List<FlowRule> rules = new ArrayList<>();
477
478 // Build selector
479 TrafficSelector.Builder sb = DefaultTrafficSelector.builder();
480 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) fwd.selector().getCriterion(Criterion.Type.VLAN_VID);
481 if (vlanIdCriterion == null) {
482 log.error("Egress forwarding objective:{} must include vlanId", fwd.id());
483 fail(fwd, ObjectiveError.BADPARAMS);
484 return rules;
485 }
486
487 Optional<Instruction> outInstr = fwd.treatment().allInstructions().stream()
488 .filter(instruction -> instruction instanceof Instructions.OutputInstruction).findFirst();
489 if (!outInstr.isPresent()) {
490 log.error("Egress forwarding objective:{} must include output port", fwd.id());
491 fail(fwd, ObjectiveError.BADPARAMS);
492 return rules;
493 }
494
495 PortNumber portNumber = ((Instructions.OutputInstruction) outInstr.get()).port();
496
497 sb.matchVlanId(vlanIdCriterion.vlanId());
498 OfdpaMatchActsetOutput actsetOutput = new OfdpaMatchActsetOutput(portNumber);
499 sb.extension(actsetOutput, deviceId);
500
501 // Build a flow rule for Egress VLAN Flow table
502 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
503 tb.transition(UNICAST_ROUTING_TABLE_1);
504 if (fwd.treatment() != null) {
505 for (Instruction instr : fwd.treatment().allInstructions()) {
506 if (instr instanceof L2ModificationInstruction &&
507 ((L2ModificationInstruction) instr).subtype() ==
508 L2ModificationInstruction.L2SubType.VLAN_ID) {
509 tb.immediate().add(instr);
510 }
511 if (instr instanceof L2ModificationInstruction &&
512 ((L2ModificationInstruction) instr).subtype() ==
513 L2ModificationInstruction.L2SubType.VLAN_PUSH) {
514 EthType ethType = ((L2ModificationInstruction.ModVlanHeaderInstruction) instr).ethernetType();
515 tb.immediate().pushVlan(ethType);
516 }
517 }
518 }
519
520 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
521 .fromApp(fwd.appId())
522 .withPriority(fwd.priority())
523 .forDevice(deviceId)
524 .withSelector(sb.build())
525 .withTreatment(tb.build())
526 .makePermanent()
527 .forTable(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
528 rules.add(ruleBuilder.build());
529 return rules;
530 }
531
532 /**
533 * Handles forwarding rules to the IP Unicast Routing.
534 *
535 * @param fwd the forwarding objective
536 * @return A collection of flow rules, or an empty set
537 */
538 protected Collection<FlowRule> processDoubleTaggedFwd(ForwardingObjective fwd) {
539 // inner for UNICAST_ROUTING_TABLE_1, outer for UNICAST_ROUTING_TABLE
540 TrafficSelector selector = fwd.selector();
541 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
542 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
543 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
544
545 EthTypeCriterion ethType =
546 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
547
548 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
549 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
550 sBuilder.matchVlanId(VlanId.ANY);
551 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
552 if (!ipv4Dst.isMulticast() && ipv4Dst.prefixLength() == 32) {
553 sBuilder.matchIPDst(ipv4Dst);
554 if (fwd.nextId() != null) {
555 NextGroup next = getGroupForNextObjective(fwd.nextId());
556 if (next != null) {
557 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
558 // we only need the top level group's key to point the flow to it
559 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
560 if (group == null) {
561 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
562 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
563 fail(fwd, ObjectiveError.GROUPMISSING);
564 return Collections.emptySet();
565 }
566 outerTtb.immediate().setVlanId(extractDummyVlanIdFromGroupId(group.id().id()));
567 //ACTSET_OUTPUT in OVS will match output action in write_action() set.
568 outerTtb.deferred().setOutput(extractOutputPortFromGroupId(group.id().id()));
569 outerTtb.transition(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
570 innerTtb.deferred().group(group.id());
571 innerTtb.transition(ACL_TABLE);
572
573 FlowRule.Builder innerRuleBuilder = DefaultFlowRule.builder()
574 .fromApp(fwd.appId())
575 .withPriority(fwd.priority())
576 .forDevice(deviceId)
577 .withSelector(sBuilder.build())
578 .withTreatment(innerTtb.build())
579 .forTable(UNICAST_ROUTING_TABLE_1);
580 if (fwd.permanent()) {
581 innerRuleBuilder.makePermanent();
582 } else {
583 innerRuleBuilder.makeTemporary(fwd.timeout());
584 }
585 Collection<FlowRule> flowRuleCollection = new HashSet<>();
586 flowRuleCollection.add(innerRuleBuilder.build());
587
588 FlowRule.Builder outerRuleBuilder = DefaultFlowRule.builder()
589 .fromApp(fwd.appId())
590 .withPriority(fwd.priority())
591 .forDevice(deviceId)
592 .withSelector(sBuilder.build())
593 .withTreatment(outerTtb.build())
594 .forTable(UNICAST_ROUTING_TABLE);
595 if (fwd.permanent()) {
596 outerRuleBuilder.makePermanent();
597 } else {
598 outerRuleBuilder.makeTemporary(fwd.timeout());
599 }
600 flowRuleCollection.add(innerRuleBuilder.build());
601 flowRuleCollection.add(outerRuleBuilder.build());
602 return flowRuleCollection;
603 } else {
604 log.warn("Cannot find group for nextId:{} in dev:{}. Aborting fwd:{}",
605 fwd.nextId(), deviceId, fwd.id());
606 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
607 return Collections.emptySet();
608 }
609 } else {
610 log.warn("NextId is not specified in fwd:{}", fwd.id());
611 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
612 return Collections.emptySet();
613 }
614 }
615 }
616 return Collections.emptySet();
617 }
618
619 private static VlanId extractDummyVlanIdFromGroupId(int groupId) {
620 short vlanId = (short) ((groupId & 0x7FF8000) >> 15);
621 return VlanId.vlanId(vlanId);
622 }
623
624 private static PortNumber extractOutputPortFromGroupId(int groupId) {
625 return PortNumber.portNumber(groupId & 0x7FFF);
626 }
627
Saurav Das4ce45962015-11-24 23:21:05 -0800628 /*
Charles Chanab591602019-01-22 17:25:04 -0800629 * Open vSwitch emulation does not require the non OF-standard rules for
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700630 * matching untagged packets that ofdpa uses.
Saurav Das4ce45962015-11-24 23:21:05 -0800631 *
632 * (non-Javadoc)
633 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter
634 */
Saurav Das558afec2015-05-31 17:12:48 -0700635 @Override
Charles Chan66291502018-03-02 16:43:28 -0800636 protected List<List<FlowRule>> processVlanIdFilter(PortCriterion portCriterion,
pier6aef5b72019-06-10 17:10:26 +0200637 VlanIdCriterion vidCriterion,
638 VlanId assignedVlan,
639 ApplicationId applicationId,
640 boolean install) {
Charles Chan79769232016-07-05 16:34:39 -0700641 List<FlowRule> rules = new ArrayList<>();
Saurav Das2857f382015-11-03 14:39:27 -0800642 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
643 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
644 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800645 treatment.transition(TMAC_TABLE);
646
Saurav Das2857f382015-11-03 14:39:27 -0800647 if (vidCriterion.vlanId() == VlanId.NONE) {
648 // untagged packets are assigned vlans
649 treatment.pushVlan().setVlanId(assignedVlan);
Piere5bff482018-03-07 11:42:50 +0100650 } else if (!vidCriterion.vlanId().equals(assignedVlan)) {
651 // Rewrite with assigned vlans
652 treatment.setVlanId(assignedVlan);
Saurav Das2857f382015-11-03 14:39:27 -0800653 }
Saurav Das2857f382015-11-03 14:39:27 -0800654
655 // ofdpa cannot match on ALL portnumber, so we need to use separate
656 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800657 List<PortNumber> portnums = new ArrayList<>();
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700658 if (portCriterion != null) {
659 if (portCriterion.port() == PortNumber.ALL) {
660 for (Port port : deviceService.getPorts(deviceId)) {
661 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
662 portnums.add(port.number());
663 }
Saurav Das2857f382015-11-03 14:39:27 -0800664 }
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700665 } else {
666 portnums.add(portCriterion.port());
Saurav Das2857f382015-11-03 14:39:27 -0800667 }
Saurav Das2857f382015-11-03 14:39:27 -0800668 }
Saurav Das4f980082015-11-05 13:39:15 -0800669
Saurav Das2857f382015-11-03 14:39:27 -0800670 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800671 // NOTE: Emulating OFDPA behavior by popping off internal assigned
672 // VLAN before sending to controller
Charles Chanab591602019-01-22 17:25:04 -0800673 if (vidCriterion.vlanId() == VlanId.NONE) {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700674 try {
675 groupCheckerLock.lock();
676 if (flowRuleQueue == null) {
677 // this means that the group has been created
678 // and that groupChecker has destroyed the queue
679 log.debug("Installing punt table rule for untagged port {} and vlan {}.",
680 pnum, assignedVlan);
681 rules.add(buildPuntTableRule(pnum, assignedVlan));
682 } else {
683 // The VLAN punt group may be held back due to device initial audit.
684 // In that case, we queue all punt table flow until the group has been created.
685 log.debug("popVlanPuntGroup not found in dev:{}, queueing this flow rule.", deviceId);
686 flowRuleQueue.add(buildPuntTableRule(pnum, assignedVlan));
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700687 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700688 } finally {
689 groupCheckerLock.unlock();
Charles Chan0f43e472017-02-14 14:00:16 -0800690 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700691 } else if (vidCriterion.vlanId() != VlanId.NONE) {
692 // for tagged ports just forward to the controller
693 log.debug("Installing punt rule for tagged port {} and vlan {}.", pnum, vidCriterion.vlanId());
694 rules.add(buildPuntTableRuleTagged(pnum, vidCriterion.vlanId()));
Charles Chan0f43e472017-02-14 14:00:16 -0800695 }
696
Saurav Das4f980082015-11-05 13:39:15 -0800697 // create rest of flowrule
Saurav Das2857f382015-11-03 14:39:27 -0800698 selector.matchInPort(pnum);
699 FlowRule rule = DefaultFlowRule.builder()
700 .forDevice(deviceId)
701 .withSelector(selector.build())
702 .withTreatment(treatment.build())
703 .withPriority(DEFAULT_PRIORITY)
704 .fromApp(applicationId)
705 .makePermanent()
706 .forTable(VLAN_TABLE).build();
707 rules.add(rule);
708 }
Charles Chanf57a8252016-06-29 19:12:37 -0700709
Charles Chan66291502018-03-02 16:43:28 -0800710 return ImmutableList.of(rules);
Saurav Das2857f382015-11-03 14:39:27 -0800711 }
712
Pier Ventree0ae7a32016-11-23 09:57:42 -0800713 /**
Charles Chan0f43e472017-02-14 14:00:16 -0800714 * Creates punt table entry that matches IN_PORT and VLAN_VID and points to
715 * a group that pop vlan and punt.
716 *
717 * @param portNumber port number
718 * @param assignedVlan internally assigned vlan id
719 * @return punt table flow rule
720 */
721 private FlowRule buildPuntTableRule(PortNumber portNumber, VlanId assignedVlan) {
722 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
723 .matchInPort(portNumber)
724 .matchVlanId(assignedVlan);
725 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
Yi Tsengfa394de2017-02-01 11:26:40 -0800726 .group(new GroupId(POP_VLAN_PUNT_GROUP_ID));
Charles Chan0f43e472017-02-14 14:00:16 -0800727
728 return DefaultFlowRule.builder()
729 .forDevice(deviceId)
730 .withSelector(sbuilder.build())
731 .withTreatment(tbuilder.build())
732 .withPriority(PacketPriority.CONTROL.priorityValue())
733 .fromApp(driverId)
734 .makePermanent()
735 .forTable(PUNT_TABLE).build();
736 }
737
738 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700739 * Creates punt table entry that matches IN_PORT and VLAN_VID and forwards
740 * packet to controller tagged.
741 *
742 * @param portNumber port number
743 * @param packetVlan vlan tag of the packet
744 * @return punt table flow rule
745 */
746 private FlowRule buildPuntTableRuleTagged(PortNumber portNumber, VlanId packetVlan) {
747 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
748 .matchInPort(portNumber)
749 .matchVlanId(packetVlan);
750 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder().punt();
751
752 return DefaultFlowRule.builder()
753 .forDevice(deviceId)
754 .withSelector(sbuilder.build())
755 .withTreatment(tbuilder.build())
756 .withPriority(PacketPriority.CONTROL.priorityValue())
757 .fromApp(driverId)
758 .makePermanent()
759 .forTable(PUNT_TABLE).build();
760 }
761
Saurav Das8a0732e2015-11-20 15:27:53 -0800762 @Override
Charles Chan66291502018-03-02 16:43:28 -0800763 protected List<List<FlowRule>> processEthDstFilter(PortCriterion portCriterion,
Saurav Das4ce45962015-11-24 23:21:05 -0800764 EthCriterion ethCriterion,
765 VlanIdCriterion vidCriterion,
766 VlanId assignedVlan,
Charles Chan45b69ab2018-03-02 15:41:41 -0800767 MacAddress unicastMac,
Saurav Das4ce45962015-11-24 23:21:05 -0800768 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800769 // Consider PortNumber.ANY as wildcard. Match ETH_DST only
770 if (portCriterion != null && portCriterion.port() == PortNumber.ANY) {
771 return processEthDstOnlyFilter(ethCriterion, applicationId);
772 }
773
Charles Chan5b9df8d2016-03-28 22:21:40 -0700774 // Multicast MAC
775 if (ethCriterion.mask() != null) {
Charles Chan45b69ab2018-03-02 15:41:41 -0800776 return processMcastEthDstFilter(ethCriterion, assignedVlan, unicastMac, applicationId);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700777 }
778
Saurav Das4ce45962015-11-24 23:21:05 -0800779 //handling untagged packets via assigned VLAN
780 if (vidCriterion.vlanId() == VlanId.NONE) {
781 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
782 }
783 // ofdpa cannot match on ALL portnumber, so we need to use separate
784 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800785 List<PortNumber> portnums = new ArrayList<>();
Ray Milkey74e59132018-01-17 15:24:52 -0800786 if (portCriterion != null) {
787 if (portCriterion.port() == PortNumber.ALL) {
788 for (Port port : deviceService.getPorts(deviceId)) {
789 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
790 portnums.add(port.number());
791 }
Saurav Das4ce45962015-11-24 23:21:05 -0800792 }
Ray Milkey74e59132018-01-17 15:24:52 -0800793 } else {
794 portnums.add(portCriterion.port());
Saurav Das4ce45962015-11-24 23:21:05 -0800795 }
Saurav Das4ce45962015-11-24 23:21:05 -0800796 }
797
Charles Chan50d900c2018-03-02 13:26:22 -0800798 List<FlowRule> rules = new ArrayList<>();
Saurav Das4ce45962015-11-24 23:21:05 -0800799 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800800 // TMAC rules for unicast IP packets
Saurav Das4ce45962015-11-24 23:21:05 -0800801 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
802 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
803 selector.matchInPort(pnum);
804 selector.matchVlanId(vidCriterion.vlanId());
805 selector.matchEthType(Ethernet.TYPE_IPV4);
806 selector.matchEthDst(ethCriterion.mac());
Saurav Das4ce45962015-11-24 23:21:05 -0800807 treatment.transition(UNICAST_ROUTING_TABLE);
808 FlowRule rule = DefaultFlowRule.builder()
809 .forDevice(deviceId)
810 .withSelector(selector.build())
811 .withTreatment(treatment.build())
812 .withPriority(DEFAULT_PRIORITY)
813 .fromApp(applicationId)
814 .makePermanent()
815 .forTable(TMAC_TABLE).build();
816 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800817
818 // TMAC rules for MPLS packets
Saurav Das4ce45962015-11-24 23:21:05 -0800819 selector = DefaultTrafficSelector.builder();
820 treatment = DefaultTrafficTreatment.builder();
821 selector.matchInPort(pnum);
822 selector.matchVlanId(vidCriterion.vlanId());
823 selector.matchEthType(Ethernet.MPLS_UNICAST);
824 selector.matchEthDst(ethCriterion.mac());
Saurav Das4ce45962015-11-24 23:21:05 -0800825 treatment.transition(MPLS_TABLE_0);
826 rule = DefaultFlowRule.builder()
827 .forDevice(deviceId)
828 .withSelector(selector.build())
829 .withTreatment(treatment.build())
830 .withPriority(DEFAULT_PRIORITY)
831 .fromApp(applicationId)
832 .makePermanent()
833 .forTable(TMAC_TABLE).build();
834 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800835
836 // TMAC rules for IPv6 packets
Pier Ventree0ae7a32016-11-23 09:57:42 -0800837 selector = DefaultTrafficSelector.builder();
838 treatment = DefaultTrafficTreatment.builder();
839 selector.matchInPort(pnum);
840 selector.matchVlanId(vidCriterion.vlanId());
841 selector.matchEthType(Ethernet.TYPE_IPV6);
842 selector.matchEthDst(ethCriterion.mac());
Pier Ventree0ae7a32016-11-23 09:57:42 -0800843 treatment.transition(UNICAST_ROUTING_TABLE);
844 rule = DefaultFlowRule.builder()
845 .forDevice(deviceId)
846 .withSelector(selector.build())
847 .withTreatment(treatment.build())
848 .withPriority(DEFAULT_PRIORITY)
849 .fromApp(applicationId)
850 .makePermanent()
851 .forTable(TMAC_TABLE).build();
852 rules.add(rule);
Saurav Das4ce45962015-11-24 23:21:05 -0800853 }
Charles Chan66291502018-03-02 16:43:28 -0800854 return ImmutableList.of(rules);
Saurav Das4ce45962015-11-24 23:21:05 -0800855 }
856
Charles Chan5270ed02016-01-30 23:22:37 -0800857 @Override
Charles Chan66291502018-03-02 16:43:28 -0800858 protected List<List<FlowRule>> processEthDstOnlyFilter(EthCriterion ethCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700859 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800860 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
861 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
862 selector.matchEthType(Ethernet.TYPE_IPV4);
863 selector.matchEthDst(ethCriterion.mac());
Charles Chan5270ed02016-01-30 23:22:37 -0800864 treatment.transition(UNICAST_ROUTING_TABLE);
865 FlowRule rule = DefaultFlowRule.builder()
866 .forDevice(deviceId)
867 .withSelector(selector.build())
868 .withTreatment(treatment.build())
869 .withPriority(DEFAULT_PRIORITY)
870 .fromApp(applicationId)
871 .makePermanent()
872 .forTable(TMAC_TABLE).build();
Charles Chan66291502018-03-02 16:43:28 -0800873 return ImmutableList.of(ImmutableList.of(rule));
Charles Chan5270ed02016-01-30 23:22:37 -0800874 }
875
Saurav Das4ce45962015-11-24 23:21:05 -0800876 /*
Charles Chanab591602019-01-22 17:25:04 -0800877 * Open vSwitch emulation allows MPLS ECMP.
Saurav Das4ce45962015-11-24 23:21:05 -0800878 *
879 * (non-Javadoc)
880 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthTypeSpecific
881 */
882 @Override
883 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
Andrea Campanella7c977b92018-05-30 21:39:45 -0700884 if (isDoubleTagged(fwd)) {
885 return processDoubleTaggedFwd(fwd);
886 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800887 TrafficSelector selector = fwd.selector();
888 EthTypeCriterion ethType =
889 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
890 if ((ethType == null) ||
891 (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
Pier Ventree0ae7a32016-11-23 09:57:42 -0800892 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST) &&
893 (ethType.ethType().toShort() != Ethernet.TYPE_IPV6)) {
Saurav Das4ce45962015-11-24 23:21:05 -0800894 log.warn("processSpecific: Unsupported forwarding objective criteria"
895 + "ethType:{} in dev:{}", ethType, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800896 fail(fwd, ObjectiveError.UNSUPPORTED);
897 return Collections.emptySet();
898 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800899 boolean defaultRule = false;
Charles Chan50d900c2018-03-02 13:26:22 -0800900 int forTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800901 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
Flavio Castroe10fa242016-01-15 12:43:51 -0800902 TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder();
903
Saurav Das8a0732e2015-11-20 15:27:53 -0800904 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Flavio Castroe10fa242016-01-15 12:43:51 -0800905 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700906 if (ipv4Dst.isMulticast()) {
907 if (ipv4Dst.prefixLength() != 32) {
908 log.warn("Multicast specific forwarding objective can only be /32");
909 fail(fwd, ObjectiveError.BADPARAMS);
910 return ImmutableSet.of();
911 }
912 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
913 if (assignedVlan == null) {
914 log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
915 fail(fwd, ObjectiveError.BADPARAMS);
916 return ImmutableSet.of();
917 }
918 filteredSelector.matchVlanId(assignedVlan);
919 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
920 forTableId = MULTICAST_ROUTING_TABLE;
921 log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
922 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800923 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700924 if (ipv4Dst.prefixLength() == 0) {
925 // The entire IPV4_DST field is wildcarded intentionally
926 filteredSelector.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700927 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700928 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700929 }
930 forTableId = UNICAST_ROUTING_TABLE;
931 log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
932 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800933 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800934 } else if (ethType.ethType().toShort() == Ethernet.TYPE_IPV6) {
Julia Ferguson65428c32017-08-10 18:15:24 +0000935 IpPrefix ipv6Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV6_DST)).ip();
936 if (ipv6Dst.isMulticast()) {
937 if (ipv6Dst.prefixLength() != IpAddress.INET6_BIT_LENGTH) {
938 log.debug("Multicast specific IPv6 forwarding objective can only be /128");
939 fail(fwd, ObjectiveError.BADPARAMS);
940 return ImmutableSet.of();
941 }
942 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
943 if (assignedVlan == null) {
944 log.debug("VLAN ID required by multicast specific fwd obj is missing. Abort.");
945 fail(fwd, ObjectiveError.BADPARAMS);
946 return ImmutableSet.of();
947 }
948 filteredSelector.matchVlanId(assignedVlan);
949 filteredSelector.matchEthType(Ethernet.TYPE_IPV6).matchIPv6Dst(ipv6Dst);
950 forTableId = MULTICAST_ROUTING_TABLE;
951 log.debug("processing IPv6 multicast specific forwarding objective {} -> next:{}"
952 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
953 } else {
954 if (buildIpv6Selector(filteredSelector, fwd) < 0) {
955 return Collections.emptyList();
956 }
957 forTableId = UNICAST_ROUTING_TABLE;
Pier Ventree0ae7a32016-11-23 09:57:42 -0800958 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800959 } else {
960 filteredSelector
961 .matchEthType(Ethernet.MPLS_UNICAST)
962 .matchMplsLabel(((MplsCriterion)
963 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
964 MplsBosCriterion bos = (MplsBosCriterion) selector
965 .getCriterion(Criterion.Type.MPLS_BOS);
966 if (bos != null) {
967 filteredSelector.matchMplsBos(bos.mplsBos());
968 }
969 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800970 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
971 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800972 }
973
974 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
975 if (fwd.treatment() != null) {
976 for (Instruction i : fwd.treatment().allInstructions()) {
Charles Chanab591602019-01-22 17:25:04 -0800977 if (i instanceof L3ModificationInstruction) {
Charles Chan40132b32017-01-22 00:19:37 -0800978 L3ModificationInstruction l3instr = (L3ModificationInstruction) i;
979 if (l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_IN) ||
980 l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_OUT)) {
981 continue;
982 }
983 }
Charles Chan7d10b162015-12-07 18:54:45 -0800984 /*
985 * NOTE: OF-DPA does not support immediate instruction in
986 * L3 unicast and MPLS table.
987 */
988 tb.deferred().add(i);
Saurav Das8a0732e2015-11-20 15:27:53 -0800989 }
990 }
991
992 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800993 NextGroup next = getGroupForNextObjective(fwd.nextId());
994 if (next != null) {
995 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
996 // we only need the top level group's key to point the flow to it
997 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
998 if (group == null) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700999 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1000 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -08001001 fail(fwd, ObjectiveError.GROUPMISSING);
1002 return Collections.emptySet();
1003 }
1004 tb.deferred().group(group.id());
Saurav Das8a0732e2015-11-20 15:27:53 -08001005 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001006 }
1007 tb.transition(ACL_TABLE);
1008 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
1009 .fromApp(fwd.appId())
1010 .withPriority(fwd.priority())
1011 .forDevice(deviceId)
1012 .withSelector(filteredSelector.build())
1013 .withTreatment(tb.build())
1014 .forTable(forTableId);
1015
1016 if (fwd.permanent()) {
1017 ruleBuilder.makePermanent();
1018 } else {
1019 ruleBuilder.makeTemporary(fwd.timeout());
1020 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001021 Collection<FlowRule> flowRuleCollection = new ArrayList<>();
1022 flowRuleCollection.add(ruleBuilder.build());
1023 if (defaultRule) {
Pier Ventree0ae7a32016-11-23 09:57:42 -08001024 flowRuleCollection.add(
1025 defaultRoute(fwd, complementarySelector, forTableId, tb)
1026 );
Flavio Castroe10fa242016-01-15 12:43:51 -08001027 log.debug("Default rule 0.0.0.0/0 is being installed two rules");
1028 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001029 return flowRuleCollection;
Saurav Das8a0732e2015-11-20 15:27:53 -08001030 }
1031
Charles Chan1e492d32016-01-30 23:22:37 -08001032 @Override
1033 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
1034 List<FlowRule> rules = new ArrayList<>();
1035
1036 // Build filtered selector
1037 TrafficSelector selector = fwd.selector();
1038 EthCriterion ethCriterion = (EthCriterion) selector
1039 .getCriterion(Criterion.Type.ETH_DST);
1040 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
Andrea Campanella7c977b92018-05-30 21:39:45 -07001041 .getCriterion(VLAN_VID);
Charles Chan1e492d32016-01-30 23:22:37 -08001042
1043 if (vlanIdCriterion == null) {
1044 log.warn("Forwarding objective for bridging requires vlan. Not "
1045 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
1046 fail(fwd, ObjectiveError.BADPARAMS);
1047 return Collections.emptySet();
1048 }
1049
1050 TrafficSelector.Builder filteredSelectorBuilder =
1051 DefaultTrafficSelector.builder();
1052 // Do not match MacAddress for subnet broadcast entry
Charles Chand05f54b2017-02-13 11:56:54 -08001053 if (!ethCriterion.mac().equals(NONE) && !ethCriterion.mac().equals(BROADCAST)) {
Charles Chan1e492d32016-01-30 23:22:37 -08001054 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
1055 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
1056 fwd.id(), fwd.nextId(), deviceId);
1057 } else {
1058 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
1059 + "in dev:{} for vlan:{}",
1060 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
1061 }
1062 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
1063 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
1064
1065 if (fwd.treatment() != null) {
1066 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
1067 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
1068 }
1069
1070 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1071 if (fwd.nextId() != null) {
1072 NextGroup next = getGroupForNextObjective(fwd.nextId());
1073 if (next != null) {
1074 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1075 // we only need the top level group's key to point the flow to it
1076 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1077 if (group != null) {
1078 treatmentBuilder.deferred().group(group.id());
1079 } else {
1080 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1081 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
1082 fail(fwd, ObjectiveError.GROUPMISSING);
1083 return Collections.emptySet();
1084 }
1085 }
1086 }
1087 treatmentBuilder.immediate().transition(ACL_TABLE);
1088 TrafficTreatment filteredTreatment = treatmentBuilder.build();
1089
1090 // Build bridging table entries
1091 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
1092 flowRuleBuilder.fromApp(fwd.appId())
1093 .withPriority(fwd.priority())
1094 .forDevice(deviceId)
1095 .withSelector(filteredSelector)
1096 .withTreatment(filteredTreatment)
1097 .forTable(BRIDGING_TABLE);
1098 if (fwd.permanent()) {
1099 flowRuleBuilder.makePermanent();
1100 } else {
1101 flowRuleBuilder.makeTemporary(fwd.timeout());
1102 }
1103 rules.add(flowRuleBuilder.build());
1104 return rules;
1105 }
1106
Saurav Das52025962016-01-28 22:30:01 -08001107 @Override
Charles Chanab591602019-01-22 17:25:04 -08001108 protected TrafficTreatment.Builder versatileTreatmentBuilder(ForwardingObjective fwd) {
Saurav Das52025962016-01-28 22:30:01 -08001109 // XXX driver does not currently do type checking as per Tables 65-67 in
1110 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
1111 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
1112 if (fwd.treatment() != null) {
1113 for (Instruction ins : fwd.treatment().allInstructions()) {
1114 if (ins instanceof OutputInstruction) {
1115 OutputInstruction o = (OutputInstruction) ins;
Charles Chanab591602019-01-22 17:25:04 -08001116 if (PortNumber.CONTROLLER.equals(o.port())) {
Charles Chan0f43e472017-02-14 14:00:16 -08001117 ttBuilder.transition(PUNT_TABLE);
Saurav Das52025962016-01-28 22:30:01 -08001118 } else {
1119 log.warn("Only allowed treatments in versatile forwarding "
1120 + "objectives are punts to the controller");
1121 }
Charles Chanf76de302018-06-15 18:54:18 -07001122 } else if (ins instanceof NoActionInstruction) {
1123 // No action is allowed and nothing needs to be done
Saurav Das52025962016-01-28 22:30:01 -08001124 } else {
1125 log.warn("Cannot process instruction in versatile fwd {}", ins);
1126 }
1127 }
Charles Chan2df0e8a2017-01-09 11:45:08 -08001128 if (fwd.treatment().clearedDeferred()) {
1129 ttBuilder.wipeDeferred();
1130 }
Saurav Das52025962016-01-28 22:30:01 -08001131 }
1132 if (fwd.nextId() != null) {
Charles Chanab591602019-01-22 17:25:04 -08001133 // Override case
Saurav Das52025962016-01-28 22:30:01 -08001134 NextGroup next = getGroupForNextObjective(fwd.nextId());
Charles Chanab591602019-01-22 17:25:04 -08001135 if (next == null) {
1136 fail(fwd, ObjectiveError.BADPARAMS);
1137 return null;
1138 }
Saurav Das52025962016-01-28 22:30:01 -08001139 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1140 // we only need the top level group's key to point the flow to it
1141 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1142 if (group == null) {
1143 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
Charles Chanab591602019-01-22 17:25:04 -08001144 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das52025962016-01-28 22:30:01 -08001145 fail(fwd, ObjectiveError.GROUPMISSING);
Charles Chanab591602019-01-22 17:25:04 -08001146 return null;
Saurav Das52025962016-01-28 22:30:01 -08001147 }
1148 ttBuilder.deferred().group(group.id());
1149 }
Charles Chanab591602019-01-22 17:25:04 -08001150 return ttBuilder;
Saurav Das52025962016-01-28 22:30:01 -08001151 }
1152
1153 /*
Charles Chanab591602019-01-22 17:25:04 -08001154 * Open vSwitch emulation requires table-miss-entries in forwarding tables.
Saurav Das52025962016-01-28 22:30:01 -08001155 * Real OFDPA does not require these rules as they are put in by default.
1156 *
1157 * (non-Javadoc)
1158 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#initializePipeline()
1159 */
Saurav Das2857f382015-11-03 14:39:27 -08001160 @Override
Saurav Das558afec2015-05-31 17:12:48 -07001161 protected void initializePipeline() {
Charles Chanf6ec1532017-02-08 16:10:40 -08001162 initTableMiss(PORT_TABLE, VLAN_TABLE, null);
1163 initTableMiss(VLAN_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001164 initTableMiss(VLAN_1_TABLE, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001165 initTableMiss(TMAC_TABLE, BRIDGING_TABLE, null);
1166 initTableMiss(UNICAST_ROUTING_TABLE, ACL_TABLE, null);
1167 initTableMiss(MULTICAST_ROUTING_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001168 initTableMiss(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS, ACL_TABLE, null);
1169 initTableMiss(UNICAST_ROUTING_TABLE_1, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001170 initTableMiss(MPLS_TABLE_0, MPLS_TABLE_1, null);
1171 initTableMiss(MPLS_TABLE_1, ACL_TABLE, null);
1172 initTableMiss(BRIDGING_TABLE, ACL_TABLE, null);
1173 initTableMiss(ACL_TABLE, -1, null);
Charles Chan7f9d8462019-10-17 20:09:45 -07001174 initPuntTable();
Charles Chan0f43e472017-02-14 14:00:16 -08001175
Charles Chanab591602019-01-22 17:25:04 -08001176 initPopVlanPuntGroup();
Saurav Das558afec2015-05-31 17:12:48 -07001177 }
1178
Charles Chanf6ec1532017-02-08 16:10:40 -08001179 /**
1180 * Install table-miss flow entry.
1181 *
1182 * If treatment exists, use it directly.
1183 * Else if treatment does not exist but nextTable > 0, transit to next table.
1184 * Else apply empty treatment.
1185 *
1186 * @param thisTable this table ID
1187 * @param nextTable next table ID
1188 * @param treatment traffic treatment to apply.
1189 */
1190 private void initTableMiss(int thisTable, int nextTable, TrafficTreatment treatment) {
Saurav Das558afec2015-05-31 17:12:48 -07001191 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chanf6ec1532017-02-08 16:10:40 -08001192 TrafficSelector selector = DefaultTrafficSelector.builder().build();
Saurav Das558afec2015-05-31 17:12:48 -07001193
Charles Chanf6ec1532017-02-08 16:10:40 -08001194 if (treatment == null) {
1195 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1196 if (nextTable > 0) {
1197 tBuilder.transition(nextTable);
Saurav Das558afec2015-05-31 17:12:48 -07001198 }
Charles Chanf6ec1532017-02-08 16:10:40 -08001199 treatment = tBuilder.build();
1200 }
Saurav Das558afec2015-05-31 17:12:48 -07001201
Charles Chanb7504392017-02-10 12:51:04 -08001202 FlowRule rule = DefaultFlowRule.builder()
1203 .forDevice(deviceId)
Charles Chanf6ec1532017-02-08 16:10:40 -08001204 .withSelector(selector)
1205 .withTreatment(treatment)
Charles Chanb7504392017-02-10 12:51:04 -08001206 .withPriority(LOWEST_PRIORITY)
1207 .fromApp(driverId)
1208 .makePermanent()
Charles Chanf6ec1532017-02-08 16:10:40 -08001209 .forTable(thisTable).build();
Charles Chanb7504392017-02-10 12:51:04 -08001210 ops = ops.add(rule);
Saurav Das2857f382015-11-03 14:39:27 -08001211
1212 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1213 @Override
1214 public void onSuccess(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001215 log.info("Initialized table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001216 }
Saurav Das2857f382015-11-03 14:39:27 -08001217 @Override
1218 public void onError(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001219 log.warn("Failed to initialize table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001220 }
1221 }));
1222 }
Charles Chan0f43e472017-02-14 14:00:16 -08001223
1224 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001225 * Install lldp/bbdp matching rules at table PUNT_TABLE
1226 * that forward traffic to controller.
1227 *
1228 */
Charles Chan7f9d8462019-10-17 20:09:45 -07001229 private void initPuntTable() {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001230 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1231 TrafficTreatment treatment = DefaultTrafficTreatment.builder().punt().build();
1232
Charles Chan7f9d8462019-10-17 20:09:45 -07001233 // Add punt rule for LLDP and BDDP
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001234 TrafficSelector.Builder lldpSelector = DefaultTrafficSelector.builder();
1235 lldpSelector.matchEthType(EthType.EtherType.LLDP.ethType().toShort());
1236 FlowRule lldpRule = DefaultFlowRule.builder()
1237 .forDevice(deviceId)
1238 .withSelector(lldpSelector.build())
1239 .withTreatment(treatment)
1240 .withPriority(HIGHEST_PRIORITY)
1241 .fromApp(driverId)
1242 .makePermanent()
1243 .forTable(PUNT_TABLE).build();
1244 ops = ops.add(lldpRule);
1245
1246 TrafficSelector.Builder bbdpSelector = DefaultTrafficSelector.builder();
1247 bbdpSelector.matchEthType(EthType.EtherType.BDDP.ethType().toShort());
1248 FlowRule bbdpRule = DefaultFlowRule.builder()
1249 .forDevice(deviceId)
1250 .withSelector(bbdpSelector.build())
1251 .withTreatment(treatment)
1252 .withPriority(HIGHEST_PRIORITY)
1253 .fromApp(driverId)
1254 .makePermanent()
1255 .forTable(PUNT_TABLE).build();
1256 ops.add(bbdpRule);
1257
1258 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1259 @Override
1260 public void onSuccess(FlowRuleOperations ops) {
Charles Chan7f9d8462019-10-17 20:09:45 -07001261 log.info("Initialized table {} on {}", PUNT_TABLE, deviceId);
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001262 }
1263 @Override
1264 public void onError(FlowRuleOperations ops) {
Charles Chan7f9d8462019-10-17 20:09:45 -07001265 log.warn("Failed to initialize table {} on {}", PUNT_TABLE, deviceId);
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001266 }
1267 }));
1268 }
1269
1270 /**
Charles Chan0f43e472017-02-14 14:00:16 -08001271 * Builds a indirect group contains pop_vlan and punt actions.
1272 * <p>
1273 * Using group instead of immediate action to ensure that
1274 * the copy of packet on the data plane is not affected by the pop vlan action.
1275 */
1276 private void initPopVlanPuntGroup() {
Yi Tsengef19de12017-04-24 11:33:05 -07001277 GroupKey groupKey = popVlanPuntGroupKey();
Charles Chan0f43e472017-02-14 14:00:16 -08001278 TrafficTreatment bucketTreatment = DefaultTrafficTreatment.builder()
1279 .popVlan().punt().build();
1280 GroupBucket bucket =
1281 DefaultGroupBucket.createIndirectGroupBucket(bucketTreatment);
1282 GroupDescription groupDesc =
1283 new DefaultGroupDescription(
1284 deviceId,
1285 GroupDescription.Type.INDIRECT,
1286 new GroupBuckets(Collections.singletonList(bucket)),
1287 groupKey,
1288 POP_VLAN_PUNT_GROUP_ID,
1289 driverId);
1290 groupService.addGroup(groupDesc);
1291
1292 log.info("Initialized pop vlan punt group on {}", deviceId);
1293 }
Yi Tsengef19de12017-04-24 11:33:05 -07001294
1295 /**
1296 * Generates group key for a static indirect group that pop vlan and punt to
1297 * controller.
1298 *
1299 * @return the group key of the indirect table
1300 */
1301 private GroupKey popVlanPuntGroupKey() {
Charles Chan367c1c12018-10-19 16:23:28 -07001302 int hash = POP_VLAN_PUNT_GROUP_ID | (Objects.hash(deviceId) & FOUR_NIBBLE_MASK);
Yi Tsengef19de12017-04-24 11:33:05 -07001303 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
1304 }
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001305
1306 private class PopVlanPuntGroupChecker implements Runnable {
1307 @Override
1308 public void run() {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001309 try {
1310 groupCheckerLock.lock();
1311 // this can happen outside of the lock but I think it is safer
1312 // to include it here.
1313 Group group = groupService.getGroup(deviceId, popVlanPuntGroupKey());
Charles Chan4a288d92019-02-16 00:11:18 +00001314 if (group != null) {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001315 log.debug("PopVlanPuntGroupChecker: Installing {} missing rules at punt table.",
1316 flowRuleQueue.size());
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001317
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001318 // if we have pending flow rules install them
1319 if (flowRuleQueue.size() > 0) {
1320 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1321 // we should not care about the context here, it can only be add
1322 // since when removing the rules the group should be there already.
1323 flowRuleQueue.forEach(ops::add);
1324 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1325 @Override
1326 public void onSuccess(FlowRuleOperations ops) {
1327 log.debug("Applied {} pop vlan punt rules in device {}",
1328 ops.stages().get(0).size(), deviceId);
1329 }
1330
1331 @Override
1332 public void onError(FlowRuleOperations ops) {
1333 log.error("Failed to apply all pop vlan punt rules in dev {}", deviceId);
1334 }
1335 }));
1336 }
1337 // this signifies that the group is created and now
1338 // flow rules can be installed directly
1339 flowRuleQueue = null;
pier02a331a2020-01-23 11:59:56 +01001340 // Schedule with an initial delay the miss table flow rule installation
1341 // the delay is to make sure the queued flows are all installed before
1342 // pushing the table miss flow rule
1343 // TODO it can be further optimized by using context and completable future
1344 groupChecker.schedule(new TableMissFlowInstaller(), 5000, TimeUnit.MILLISECONDS);
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001345 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001346 } finally {
1347 groupCheckerLock.unlock();
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001348 }
1349 }
1350 }
pier02a331a2020-01-23 11:59:56 +01001351
1352 private class TableMissFlowInstaller implements Runnable {
1353 @Override
1354 public void run() {
1355 // Add table miss flow rule
1356 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1357 TrafficSelector.Builder defaultSelector = DefaultTrafficSelector.builder();
1358 TrafficTreatment treatment = DefaultTrafficTreatment.builder().punt().build();
1359 FlowRule defaultRule = DefaultFlowRule.builder()
1360 .forDevice(deviceId)
1361 .withSelector(defaultSelector.build())
1362 .withTreatment(treatment)
1363 .withPriority(LOWEST_PRIORITY)
1364 .fromApp(driverId)
1365 .makePermanent()
1366 .forTable(PUNT_TABLE).build();
1367 ops.add(defaultRule);
1368 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1369 @Override
1370 public void onSuccess(FlowRuleOperations ops) {
1371 log.info("Initialized table miss flow rule {} on {}", PUNT_TABLE, deviceId);
1372 }
1373 @Override
1374 public void onError(FlowRuleOperations ops) {
1375 log.warn("Failed to initialize table miss flow rule {} on {}", PUNT_TABLE, deviceId);
1376 }
1377 }));
1378 // shutdown the group checker gracefully
1379 groupChecker.shutdown();
1380 }
1381 }
Saurav Das558afec2015-05-31 17:12:48 -07001382}