blob: 7e43cab9ea6056190e556aa29016664fec814a79 [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) {
pier2d8ecd12020-05-20 22:50:30 +0200168 // Terminate internal references
169 // We are terminating the references here
170 // because when the device is offline the apps
171 // are still sending flowobjectives
172 if (groupChecker != null) {
173 groupChecker.shutdown();
pier1b7dd122020-02-28 09:24:11 +0100174 }
pier2d8ecd12020-05-20 22:50:30 +0200175 // create a new executor at each init and a new empty queue
176 groupChecker = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/driver",
177 "ovs-ofdpa-%d", log));
178 if (flowRuleQueue != null) {
179 flowRuleQueue.clear();
180 }
181 flowRuleQueue = new ConcurrentLinkedQueue<>();
182 groupCheckerLock = new ReentrantLock();
183 groupChecker.scheduleAtFixedRate(new PopVlanPuntGroupChecker(), 20, 50, TimeUnit.MILLISECONDS);
184 super.init(deviceId, context);
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700185 }
pier9469f3e2019-04-17 17:05:08 +0200186
Andrea Campanella7c977b92018-05-30 21:39:45 -0700187 protected void processFilter(FilteringObjective filteringObjective,
188 boolean install,
189 ApplicationId applicationId) {
pier9469f3e2019-04-17 17:05:08 +0200190 if (OfdpaPipelineUtility.isDoubleTagged(filteringObjective)) {
Andrea Campanella7c977b92018-05-30 21:39:45 -0700191 processDoubleTaggedFilter(filteringObjective, install, applicationId);
192 } else {
193 // If it is not a double-tagged filter, we fall back
194 // to the OFDPA 2.0 pipeline.
195 super.processFilter(filteringObjective, install, applicationId);
196 }
197 }
198
199 /**
Andrea Campanella7c977b92018-05-30 21:39:45 -0700200 * Determines if the forwarding objective will be used for double-tagged packets.
201 *
202 * @param fwd Forwarding objective
203 * @return True if the objective was created for double-tagged packets, false otherwise.
204 */
205 private boolean isDoubleTagged(ForwardingObjective fwd) {
206 if (fwd.nextId() != null) {
207 NextGroup next = getGroupForNextObjective(fwd.nextId());
208 if (next != null) {
209 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
210 // we only need the top level group's key
211 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
212 if (group != null) {
213 int groupId = group.id().id();
214 if (((groupId & ~TYPE_MASK) == L3_UNICAST_TYPE) &&
215 ((groupId & TYPE_L3UG_DOUBLE_VLAN_MASK) == TYPE_L3UG_DOUBLE_VLAN_MASK)) {
216 return true;
217 }
218 }
219 }
220 }
221 return false;
222 }
223
224 /**
225 * Configure filtering rules of outer and inner VLAN IDs, and a MAC address.
226 * Filtering happens in three tables (VLAN_TABLE, VLAN_1_TABLE, TMAC_TABLE).
227 *
228 * @param filteringObjective the filtering objective
229 * @param install true to add, false to remove
230 * @param applicationId for application programming this filter
231 */
232 private void processDoubleTaggedFilter(FilteringObjective filteringObjective,
233 boolean install,
234 ApplicationId applicationId) {
235 PortCriterion portCriterion = null;
236 EthCriterion ethCriterion = null;
237 VlanIdCriterion innervidCriterion = null;
238 VlanIdCriterion outerVidCriterion = null;
239 boolean popVlan = false;
240 TrafficTreatment meta = filteringObjective.meta();
241 if (!filteringObjective.key().equals(Criteria.dummy()) &&
242 filteringObjective.key().type() == Criterion.Type.IN_PORT) {
243 portCriterion = (PortCriterion) filteringObjective.key();
244 }
245 if (portCriterion == null) {
246 log.warn("No IN_PORT defined in filtering objective from app: {}" +
247 "Failed to program VLAN tables.", applicationId);
248 return;
249 } else {
250 log.debug("Received filtering objective for dev/port: {}/{}", deviceId,
251 portCriterion.port());
252 }
253
254 // meta should have only one instruction, popVlan.
255 if (meta != null && meta.allInstructions().size() == 1) {
256 L2ModificationInstruction l2Inst = (L2ModificationInstruction) meta.allInstructions().get(0);
257 if (l2Inst.subtype().equals(L2ModificationInstruction.L2SubType.VLAN_POP)) {
258 popVlan = true;
259 } else {
260 log.warn("Filtering objective can have only VLAN_POP instruction.");
261 return;
262 }
263 } else {
264 log.warn("Filtering objective should have one instruction.");
265 return;
266 }
267
268 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
269 for (Criterion criterion : filteringObjective.conditions()) {
270 switch (criterion.type()) {
271 case ETH_DST:
272 case ETH_DST_MASKED:
273 ethCriterion = (EthCriterion) criterion;
274 break;
275 case VLAN_VID:
Daniele Morofa382c22019-07-12 17:58:54 -0700276 outerVidCriterion = (VlanIdCriterion) criterion;
277 break;
278 case INNER_VLAN_VID:
279 innervidCriterion = (VlanIdCriterion) criterion;
Andrea Campanella7c977b92018-05-30 21:39:45 -0700280 break;
281 default:
282 log.warn("Unsupported filter {}", criterion);
283 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
284 return;
285 }
286 }
287
288 if (innervidCriterion == null || outerVidCriterion == null) {
289 log.warn("filtering objective should have two vidCriterion.");
290 return;
291 }
292
293 if (ethCriterion == null || ethCriterion.mac().equals(NONE)) {
294 // NOTE: it is possible that a filtering objective only has vidCriterion
295 log.warn("filtering objective missing dstMac, cannot program TMAC table");
296 return;
297 } else {
298 MacAddress unicastMac = readEthDstFromTreatment(filteringObjective.meta());
299 List<List<FlowRule>> allStages = processEthDstFilter(portCriterion, ethCriterion, innervidCriterion,
300 innervidCriterion.vlanId(), unicastMac,
301 applicationId);
302 for (List<FlowRule> flowRules : allStages) {
303 log.trace("Starting a new flow rule stage for TMAC table flow");
304 ops.newStage();
305
306 for (FlowRule flowRule : flowRules) {
307 log.trace("{} flow rules in TMAC table: {} for dev: {}",
308 (install) ? "adding" : "removing", flowRules, deviceId);
309 if (install) {
310 ops = ops.add(flowRule);
311 } else {
312 // NOTE: Only remove TMAC flow when there is no more enabled port within the
313 // same VLAN on this device if TMAC doesn't support matching on in_port.
314 if (matchInPortTmacTable()
315 || (filteringObjective.meta() != null
316 && filteringObjective.meta().clearedDeferred())) {
317 ops = ops.remove(flowRule);
318 } else {
319 log.debug("Abort TMAC flow removal on {}. Some other ports still share this TMAC flow");
320 }
321 }
322 }
323 }
324 }
325
326 List<FlowRule> rules;
327 rules = processDoubleVlanIdFilter(portCriterion, innervidCriterion,
328 outerVidCriterion, popVlan, applicationId);
329 for (FlowRule flowRule : rules) {
330 log.trace("{} flow rule in VLAN table: {} for dev: {}",
331 (install) ? "adding" : "removing", flowRule, deviceId);
332 ops = install ? ops.add(flowRule) : ops.remove(flowRule);
333 }
334
335 // apply filtering flow rules
336 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
337 @Override
338 public void onSuccess(FlowRuleOperations ops) {
339 log.debug("Applied {} filtering rules in device {}",
340 ops.stages().get(0).size(), deviceId);
341 pass(filteringObjective);
342 }
343
344 @Override
345 public void onError(FlowRuleOperations ops) {
346 log.info("Failed to apply all filtering rules in dev {}", deviceId);
347 fail(filteringObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
348 }
349 }));
350
351 }
352 /**
353 * Internal implementation of processDoubleVlanIdFilter.
354 *
355 * @param portCriterion port on device for which this filter is programmed
356 * @param innerVidCriterion inner vlan
357 * @param outerVidCriterion outer vlan
358 * @param popVlan true if outer vlan header needs to be removed
359 * @param applicationId for application programming this filter
360 * @return flow rules for port-vlan filters
361 */
362 private List<FlowRule> processDoubleVlanIdFilter(PortCriterion portCriterion,
363 VlanIdCriterion innerVidCriterion,
364 VlanIdCriterion outerVidCriterion,
365 boolean popVlan,
366 ApplicationId applicationId) {
367 List<FlowRule> rules = new ArrayList<>();
368 TrafficSelector.Builder outerSelector = DefaultTrafficSelector.builder();
369 TrafficTreatment.Builder outerTreatment = DefaultTrafficTreatment.builder();
370 TrafficSelector.Builder innerSelector = DefaultTrafficSelector.builder();
371 TrafficTreatment.Builder innerTreatment = DefaultTrafficTreatment.builder();
372
373 VlanId outerVlanId = outerVidCriterion.vlanId();
374 VlanId innerVlanId = innerVidCriterion.vlanId();
375 PortNumber portNumber = portCriterion.port();
376 // Check arguments
377 if (PortNumber.ALL.equals(portNumber)
378 || outerVlanId.equals(VlanId.NONE)
379 || innerVlanId.equals(VlanId.NONE)) {
380 log.warn("Incomplete Filtering Objective. " +
381 "VLAN Table cannot be programmed for {}", deviceId);
382 return ImmutableList.of();
383 } else {
384 outerSelector.matchInPort(portNumber);
385 innerSelector.matchInPort(portNumber);
386 outerTreatment.transition(VLAN_1_TABLE);
387 innerTreatment.transition(TMAC_TABLE);
388 outerTreatment.writeMetadata(outerVlanId.toShort(), 0xFFF);
389
390 outerSelector.matchVlanId(outerVlanId);
391 innerSelector.matchVlanId(innerVlanId);
392 //force recompilation
393 //FIXME might be issue due tu /fff mask
394 innerSelector.matchMetadata(outerVlanId.toShort());
395
396 if (popVlan) {
397 outerTreatment.popVlan();
398 }
399 }
400
401 // NOTE: for double-tagged packets, restore original outer vlan
402 // before sending it to the controller.
Charles Chanab591602019-01-22 17:25:04 -0800403 GroupKey groupKey = popVlanPuntGroupKey();
404 Group group = groupService.getGroup(deviceId, groupKey);
405 if (group != null) {
406 // push outer vlan and send to controller
407 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
408 .matchInPort(portNumber)
409 .matchVlanId(innerVlanId);
410 Host host = handler().get(HostService.class).getConnectedHosts(ConnectPoint.
411 deviceConnectPoint(deviceId + "/" + portNumber.toLong())).stream().filter(h ->
412 h.vlan().equals(outerVlanId)).findFirst().orElse(null);
413 EthType vlanType = EthType.EtherType.VLAN.ethType();
414 if (host != null) {
415 vlanType = host.tpid();
Andrea Campanella7c977b92018-05-30 21:39:45 -0700416 }
Charles Chanab591602019-01-22 17:25:04 -0800417 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
418 .pushVlan(vlanType).setVlanId(outerVlanId).punt();
419
420 rules.add(DefaultFlowRule.builder()
421 .forDevice(deviceId)
422 .withSelector(sbuilder.build())
423 .withTreatment(tbuilder.build())
424 .withPriority(PacketPriority.CONTROL.priorityValue())
425 .fromApp(driverId)
426 .makePermanent()
427 .forTable(PUNT_TABLE).build());
428 } else {
429 log.info("popVlanPuntGroup not found in dev:{}", deviceId);
430 return Collections.emptyList();
Andrea Campanella7c977b92018-05-30 21:39:45 -0700431 }
Charles Chanab591602019-01-22 17:25:04 -0800432
Andrea Campanella7c977b92018-05-30 21:39:45 -0700433 FlowRule outerRule = DefaultFlowRule.builder()
434 .forDevice(deviceId)
435 .withSelector(outerSelector.build())
436 .withTreatment(outerTreatment.build())
437 .withPriority(DEFAULT_PRIORITY)
438 .fromApp(applicationId)
439 .makePermanent()
440 .forTable(VLAN_TABLE)
441 .build();
442 FlowRule innerRule = DefaultFlowRule.builder()
443 .forDevice(deviceId)
444 .withSelector(innerSelector.build())
445 .withTreatment(innerTreatment.build())
446 .withPriority(DEFAULT_PRIORITY)
447 .fromApp(applicationId)
448 .makePermanent()
449 .forTable(VLAN_1_TABLE)
450 .build();
451 rules.add(outerRule);
452 rules.add(innerRule);
453
454 return rules;
455 }
456
457 /**
458 * In the OF-DPA 2.0 pipeline, egress forwarding objectives go to the
459 * egress tables.
460 * @param fwd the forwarding objective of type 'egress'
461 * @return a collection of flow rules to be sent to the switch. An empty
462 * collection may be returned if there is a problem in processing
463 * the flow rule
464 */
465 @Override
466 protected Collection<FlowRule> processEgress(ForwardingObjective fwd) {
467 log.debug("Processing egress forwarding objective:{} in dev:{}",
468 fwd, deviceId);
469
470 List<FlowRule> rules = new ArrayList<>();
471
472 // Build selector
473 TrafficSelector.Builder sb = DefaultTrafficSelector.builder();
474 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) fwd.selector().getCriterion(Criterion.Type.VLAN_VID);
475 if (vlanIdCriterion == null) {
476 log.error("Egress forwarding objective:{} must include vlanId", fwd.id());
477 fail(fwd, ObjectiveError.BADPARAMS);
478 return rules;
479 }
480
481 Optional<Instruction> outInstr = fwd.treatment().allInstructions().stream()
482 .filter(instruction -> instruction instanceof Instructions.OutputInstruction).findFirst();
483 if (!outInstr.isPresent()) {
484 log.error("Egress forwarding objective:{} must include output port", fwd.id());
485 fail(fwd, ObjectiveError.BADPARAMS);
486 return rules;
487 }
488
489 PortNumber portNumber = ((Instructions.OutputInstruction) outInstr.get()).port();
490
491 sb.matchVlanId(vlanIdCriterion.vlanId());
492 OfdpaMatchActsetOutput actsetOutput = new OfdpaMatchActsetOutput(portNumber);
493 sb.extension(actsetOutput, deviceId);
494
495 // Build a flow rule for Egress VLAN Flow table
496 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
497 tb.transition(UNICAST_ROUTING_TABLE_1);
498 if (fwd.treatment() != null) {
499 for (Instruction instr : fwd.treatment().allInstructions()) {
500 if (instr instanceof L2ModificationInstruction &&
501 ((L2ModificationInstruction) instr).subtype() ==
502 L2ModificationInstruction.L2SubType.VLAN_ID) {
503 tb.immediate().add(instr);
504 }
505 if (instr instanceof L2ModificationInstruction &&
506 ((L2ModificationInstruction) instr).subtype() ==
507 L2ModificationInstruction.L2SubType.VLAN_PUSH) {
508 EthType ethType = ((L2ModificationInstruction.ModVlanHeaderInstruction) instr).ethernetType();
509 tb.immediate().pushVlan(ethType);
510 }
511 }
512 }
513
514 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
515 .fromApp(fwd.appId())
516 .withPriority(fwd.priority())
517 .forDevice(deviceId)
518 .withSelector(sb.build())
519 .withTreatment(tb.build())
520 .makePermanent()
521 .forTable(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
522 rules.add(ruleBuilder.build());
523 return rules;
524 }
525
526 /**
527 * Handles forwarding rules to the IP Unicast Routing.
528 *
529 * @param fwd the forwarding objective
530 * @return A collection of flow rules, or an empty set
531 */
532 protected Collection<FlowRule> processDoubleTaggedFwd(ForwardingObjective fwd) {
533 // inner for UNICAST_ROUTING_TABLE_1, outer for UNICAST_ROUTING_TABLE
534 TrafficSelector selector = fwd.selector();
535 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
536 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
537 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
538
539 EthTypeCriterion ethType =
540 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
541
542 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
543 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
544 sBuilder.matchVlanId(VlanId.ANY);
545 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
546 if (!ipv4Dst.isMulticast() && ipv4Dst.prefixLength() == 32) {
547 sBuilder.matchIPDst(ipv4Dst);
548 if (fwd.nextId() != null) {
549 NextGroup next = getGroupForNextObjective(fwd.nextId());
550 if (next != null) {
551 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
552 // we only need the top level group's key to point the flow to it
553 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
554 if (group == null) {
555 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
556 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
557 fail(fwd, ObjectiveError.GROUPMISSING);
558 return Collections.emptySet();
559 }
560 outerTtb.immediate().setVlanId(extractDummyVlanIdFromGroupId(group.id().id()));
561 //ACTSET_OUTPUT in OVS will match output action in write_action() set.
562 outerTtb.deferred().setOutput(extractOutputPortFromGroupId(group.id().id()));
563 outerTtb.transition(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
564 innerTtb.deferred().group(group.id());
565 innerTtb.transition(ACL_TABLE);
566
567 FlowRule.Builder innerRuleBuilder = DefaultFlowRule.builder()
568 .fromApp(fwd.appId())
569 .withPriority(fwd.priority())
570 .forDevice(deviceId)
571 .withSelector(sBuilder.build())
572 .withTreatment(innerTtb.build())
573 .forTable(UNICAST_ROUTING_TABLE_1);
574 if (fwd.permanent()) {
575 innerRuleBuilder.makePermanent();
576 } else {
577 innerRuleBuilder.makeTemporary(fwd.timeout());
578 }
579 Collection<FlowRule> flowRuleCollection = new HashSet<>();
580 flowRuleCollection.add(innerRuleBuilder.build());
581
582 FlowRule.Builder outerRuleBuilder = DefaultFlowRule.builder()
583 .fromApp(fwd.appId())
584 .withPriority(fwd.priority())
585 .forDevice(deviceId)
586 .withSelector(sBuilder.build())
587 .withTreatment(outerTtb.build())
588 .forTable(UNICAST_ROUTING_TABLE);
589 if (fwd.permanent()) {
590 outerRuleBuilder.makePermanent();
591 } else {
592 outerRuleBuilder.makeTemporary(fwd.timeout());
593 }
594 flowRuleCollection.add(innerRuleBuilder.build());
595 flowRuleCollection.add(outerRuleBuilder.build());
596 return flowRuleCollection;
597 } else {
598 log.warn("Cannot find group for nextId:{} in dev:{}. Aborting fwd:{}",
599 fwd.nextId(), deviceId, fwd.id());
600 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
601 return Collections.emptySet();
602 }
603 } else {
604 log.warn("NextId is not specified in fwd:{}", fwd.id());
605 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
606 return Collections.emptySet();
607 }
608 }
609 }
610 return Collections.emptySet();
611 }
612
613 private static VlanId extractDummyVlanIdFromGroupId(int groupId) {
614 short vlanId = (short) ((groupId & 0x7FF8000) >> 15);
615 return VlanId.vlanId(vlanId);
616 }
617
618 private static PortNumber extractOutputPortFromGroupId(int groupId) {
619 return PortNumber.portNumber(groupId & 0x7FFF);
620 }
621
Saurav Das4ce45962015-11-24 23:21:05 -0800622 /*
Charles Chanab591602019-01-22 17:25:04 -0800623 * Open vSwitch emulation does not require the non OF-standard rules for
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700624 * matching untagged packets that ofdpa uses.
Saurav Das4ce45962015-11-24 23:21:05 -0800625 *
626 * (non-Javadoc)
627 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter
628 */
Saurav Das558afec2015-05-31 17:12:48 -0700629 @Override
Charles Chan66291502018-03-02 16:43:28 -0800630 protected List<List<FlowRule>> processVlanIdFilter(PortCriterion portCriterion,
pier6aef5b72019-06-10 17:10:26 +0200631 VlanIdCriterion vidCriterion,
632 VlanId assignedVlan,
633 ApplicationId applicationId,
634 boolean install) {
Charles Chan79769232016-07-05 16:34:39 -0700635 List<FlowRule> rules = new ArrayList<>();
Saurav Das2857f382015-11-03 14:39:27 -0800636 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
637 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
638 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800639 treatment.transition(TMAC_TABLE);
640
Saurav Das2857f382015-11-03 14:39:27 -0800641 if (vidCriterion.vlanId() == VlanId.NONE) {
642 // untagged packets are assigned vlans
643 treatment.pushVlan().setVlanId(assignedVlan);
Piere5bff482018-03-07 11:42:50 +0100644 } else if (!vidCriterion.vlanId().equals(assignedVlan)) {
645 // Rewrite with assigned vlans
646 treatment.setVlanId(assignedVlan);
Saurav Das2857f382015-11-03 14:39:27 -0800647 }
Saurav Das2857f382015-11-03 14:39:27 -0800648
649 // ofdpa cannot match on ALL portnumber, so we need to use separate
650 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800651 List<PortNumber> portnums = new ArrayList<>();
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700652 if (portCriterion != null) {
653 if (portCriterion.port() == PortNumber.ALL) {
654 for (Port port : deviceService.getPorts(deviceId)) {
655 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
656 portnums.add(port.number());
657 }
Saurav Das2857f382015-11-03 14:39:27 -0800658 }
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700659 } else {
660 portnums.add(portCriterion.port());
Saurav Das2857f382015-11-03 14:39:27 -0800661 }
Saurav Das2857f382015-11-03 14:39:27 -0800662 }
Saurav Das4f980082015-11-05 13:39:15 -0800663
Saurav Das2857f382015-11-03 14:39:27 -0800664 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800665 // NOTE: Emulating OFDPA behavior by popping off internal assigned
666 // VLAN before sending to controller
Charles Chanab591602019-01-22 17:25:04 -0800667 if (vidCriterion.vlanId() == VlanId.NONE) {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700668 try {
669 groupCheckerLock.lock();
670 if (flowRuleQueue == null) {
671 // this means that the group has been created
672 // and that groupChecker has destroyed the queue
673 log.debug("Installing punt table rule for untagged port {} and vlan {}.",
674 pnum, assignedVlan);
675 rules.add(buildPuntTableRule(pnum, assignedVlan));
676 } else {
677 // The VLAN punt group may be held back due to device initial audit.
678 // In that case, we queue all punt table flow until the group has been created.
679 log.debug("popVlanPuntGroup not found in dev:{}, queueing this flow rule.", deviceId);
680 flowRuleQueue.add(buildPuntTableRule(pnum, assignedVlan));
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700681 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700682 } finally {
683 groupCheckerLock.unlock();
Charles Chan0f43e472017-02-14 14:00:16 -0800684 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700685 } else if (vidCriterion.vlanId() != VlanId.NONE) {
686 // for tagged ports just forward to the controller
687 log.debug("Installing punt rule for tagged port {} and vlan {}.", pnum, vidCriterion.vlanId());
688 rules.add(buildPuntTableRuleTagged(pnum, vidCriterion.vlanId()));
Charles Chan0f43e472017-02-14 14:00:16 -0800689 }
690
Saurav Das4f980082015-11-05 13:39:15 -0800691 // create rest of flowrule
Saurav Das2857f382015-11-03 14:39:27 -0800692 selector.matchInPort(pnum);
693 FlowRule rule = DefaultFlowRule.builder()
694 .forDevice(deviceId)
695 .withSelector(selector.build())
696 .withTreatment(treatment.build())
697 .withPriority(DEFAULT_PRIORITY)
698 .fromApp(applicationId)
699 .makePermanent()
700 .forTable(VLAN_TABLE).build();
701 rules.add(rule);
702 }
Charles Chanf57a8252016-06-29 19:12:37 -0700703
Charles Chan66291502018-03-02 16:43:28 -0800704 return ImmutableList.of(rules);
Saurav Das2857f382015-11-03 14:39:27 -0800705 }
706
Pier Ventree0ae7a32016-11-23 09:57:42 -0800707 /**
Charles Chan0f43e472017-02-14 14:00:16 -0800708 * Creates punt table entry that matches IN_PORT and VLAN_VID and points to
709 * a group that pop vlan and punt.
710 *
711 * @param portNumber port number
712 * @param assignedVlan internally assigned vlan id
713 * @return punt table flow rule
714 */
715 private FlowRule buildPuntTableRule(PortNumber portNumber, VlanId assignedVlan) {
716 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
717 .matchInPort(portNumber)
718 .matchVlanId(assignedVlan);
719 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
Yi Tsengfa394de2017-02-01 11:26:40 -0800720 .group(new GroupId(POP_VLAN_PUNT_GROUP_ID));
Charles Chan0f43e472017-02-14 14:00:16 -0800721
722 return DefaultFlowRule.builder()
723 .forDevice(deviceId)
724 .withSelector(sbuilder.build())
725 .withTreatment(tbuilder.build())
726 .withPriority(PacketPriority.CONTROL.priorityValue())
727 .fromApp(driverId)
728 .makePermanent()
729 .forTable(PUNT_TABLE).build();
730 }
731
732 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700733 * Creates punt table entry that matches IN_PORT and VLAN_VID and forwards
734 * packet to controller tagged.
735 *
736 * @param portNumber port number
737 * @param packetVlan vlan tag of the packet
738 * @return punt table flow rule
739 */
740 private FlowRule buildPuntTableRuleTagged(PortNumber portNumber, VlanId packetVlan) {
741 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
742 .matchInPort(portNumber)
743 .matchVlanId(packetVlan);
744 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder().punt();
745
746 return DefaultFlowRule.builder()
747 .forDevice(deviceId)
748 .withSelector(sbuilder.build())
749 .withTreatment(tbuilder.build())
750 .withPriority(PacketPriority.CONTROL.priorityValue())
751 .fromApp(driverId)
752 .makePermanent()
753 .forTable(PUNT_TABLE).build();
754 }
755
Saurav Das8a0732e2015-11-20 15:27:53 -0800756 @Override
Charles Chan66291502018-03-02 16:43:28 -0800757 protected List<List<FlowRule>> processEthDstFilter(PortCriterion portCriterion,
Saurav Das4ce45962015-11-24 23:21:05 -0800758 EthCriterion ethCriterion,
759 VlanIdCriterion vidCriterion,
760 VlanId assignedVlan,
Charles Chan45b69ab2018-03-02 15:41:41 -0800761 MacAddress unicastMac,
Saurav Das4ce45962015-11-24 23:21:05 -0800762 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800763 // Consider PortNumber.ANY as wildcard. Match ETH_DST only
764 if (portCriterion != null && portCriterion.port() == PortNumber.ANY) {
765 return processEthDstOnlyFilter(ethCriterion, applicationId);
766 }
767
Charles Chan5b9df8d2016-03-28 22:21:40 -0700768 // Multicast MAC
769 if (ethCriterion.mask() != null) {
Charles Chan45b69ab2018-03-02 15:41:41 -0800770 return processMcastEthDstFilter(ethCriterion, assignedVlan, unicastMac, applicationId);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700771 }
772
Saurav Das4ce45962015-11-24 23:21:05 -0800773 //handling untagged packets via assigned VLAN
774 if (vidCriterion.vlanId() == VlanId.NONE) {
775 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
776 }
777 // ofdpa cannot match on ALL portnumber, so we need to use separate
778 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800779 List<PortNumber> portnums = new ArrayList<>();
Ray Milkey74e59132018-01-17 15:24:52 -0800780 if (portCriterion != null) {
781 if (portCriterion.port() == PortNumber.ALL) {
782 for (Port port : deviceService.getPorts(deviceId)) {
783 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
784 portnums.add(port.number());
785 }
Saurav Das4ce45962015-11-24 23:21:05 -0800786 }
Ray Milkey74e59132018-01-17 15:24:52 -0800787 } else {
788 portnums.add(portCriterion.port());
Saurav Das4ce45962015-11-24 23:21:05 -0800789 }
Saurav Das4ce45962015-11-24 23:21:05 -0800790 }
791
Charles Chan50d900c2018-03-02 13:26:22 -0800792 List<FlowRule> rules = new ArrayList<>();
Saurav Das4ce45962015-11-24 23:21:05 -0800793 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800794 // TMAC rules for unicast IP packets
Saurav Das4ce45962015-11-24 23:21:05 -0800795 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
796 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
797 selector.matchInPort(pnum);
798 selector.matchVlanId(vidCriterion.vlanId());
799 selector.matchEthType(Ethernet.TYPE_IPV4);
800 selector.matchEthDst(ethCriterion.mac());
Saurav Das4ce45962015-11-24 23:21:05 -0800801 treatment.transition(UNICAST_ROUTING_TABLE);
802 FlowRule rule = DefaultFlowRule.builder()
803 .forDevice(deviceId)
804 .withSelector(selector.build())
805 .withTreatment(treatment.build())
806 .withPriority(DEFAULT_PRIORITY)
807 .fromApp(applicationId)
808 .makePermanent()
809 .forTable(TMAC_TABLE).build();
810 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800811
812 // TMAC rules for MPLS packets
Saurav Das4ce45962015-11-24 23:21:05 -0800813 selector = DefaultTrafficSelector.builder();
814 treatment = DefaultTrafficTreatment.builder();
815 selector.matchInPort(pnum);
816 selector.matchVlanId(vidCriterion.vlanId());
817 selector.matchEthType(Ethernet.MPLS_UNICAST);
818 selector.matchEthDst(ethCriterion.mac());
Saurav Das4ce45962015-11-24 23:21:05 -0800819 treatment.transition(MPLS_TABLE_0);
820 rule = DefaultFlowRule.builder()
821 .forDevice(deviceId)
822 .withSelector(selector.build())
823 .withTreatment(treatment.build())
824 .withPriority(DEFAULT_PRIORITY)
825 .fromApp(applicationId)
826 .makePermanent()
827 .forTable(TMAC_TABLE).build();
828 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800829
830 // TMAC rules for IPv6 packets
Pier Ventree0ae7a32016-11-23 09:57:42 -0800831 selector = DefaultTrafficSelector.builder();
832 treatment = DefaultTrafficTreatment.builder();
833 selector.matchInPort(pnum);
834 selector.matchVlanId(vidCriterion.vlanId());
835 selector.matchEthType(Ethernet.TYPE_IPV6);
836 selector.matchEthDst(ethCriterion.mac());
Pier Ventree0ae7a32016-11-23 09:57:42 -0800837 treatment.transition(UNICAST_ROUTING_TABLE);
838 rule = DefaultFlowRule.builder()
839 .forDevice(deviceId)
840 .withSelector(selector.build())
841 .withTreatment(treatment.build())
842 .withPriority(DEFAULT_PRIORITY)
843 .fromApp(applicationId)
844 .makePermanent()
845 .forTable(TMAC_TABLE).build();
846 rules.add(rule);
Saurav Das4ce45962015-11-24 23:21:05 -0800847 }
Charles Chan66291502018-03-02 16:43:28 -0800848 return ImmutableList.of(rules);
Saurav Das4ce45962015-11-24 23:21:05 -0800849 }
850
Charles Chan5270ed02016-01-30 23:22:37 -0800851 @Override
Charles Chan66291502018-03-02 16:43:28 -0800852 protected List<List<FlowRule>> processEthDstOnlyFilter(EthCriterion ethCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700853 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800854 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
855 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
856 selector.matchEthType(Ethernet.TYPE_IPV4);
857 selector.matchEthDst(ethCriterion.mac());
Charles Chan5270ed02016-01-30 23:22:37 -0800858 treatment.transition(UNICAST_ROUTING_TABLE);
859 FlowRule rule = DefaultFlowRule.builder()
860 .forDevice(deviceId)
861 .withSelector(selector.build())
862 .withTreatment(treatment.build())
863 .withPriority(DEFAULT_PRIORITY)
864 .fromApp(applicationId)
865 .makePermanent()
866 .forTable(TMAC_TABLE).build();
Charles Chan66291502018-03-02 16:43:28 -0800867 return ImmutableList.of(ImmutableList.of(rule));
Charles Chan5270ed02016-01-30 23:22:37 -0800868 }
869
Saurav Das4ce45962015-11-24 23:21:05 -0800870 /*
Charles Chanab591602019-01-22 17:25:04 -0800871 * Open vSwitch emulation allows MPLS ECMP.
Saurav Das4ce45962015-11-24 23:21:05 -0800872 *
873 * (non-Javadoc)
874 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthTypeSpecific
875 */
876 @Override
877 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
Andrea Campanella7c977b92018-05-30 21:39:45 -0700878 if (isDoubleTagged(fwd)) {
879 return processDoubleTaggedFwd(fwd);
880 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800881 TrafficSelector selector = fwd.selector();
882 EthTypeCriterion ethType =
883 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
884 if ((ethType == null) ||
885 (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
Pier Ventree0ae7a32016-11-23 09:57:42 -0800886 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST) &&
887 (ethType.ethType().toShort() != Ethernet.TYPE_IPV6)) {
Saurav Das4ce45962015-11-24 23:21:05 -0800888 log.warn("processSpecific: Unsupported forwarding objective criteria"
889 + "ethType:{} in dev:{}", ethType, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800890 fail(fwd, ObjectiveError.UNSUPPORTED);
891 return Collections.emptySet();
892 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800893 boolean defaultRule = false;
Charles Chan50d900c2018-03-02 13:26:22 -0800894 int forTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800895 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
Flavio Castroe10fa242016-01-15 12:43:51 -0800896 TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder();
897
Saurav Das8a0732e2015-11-20 15:27:53 -0800898 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Flavio Castroe10fa242016-01-15 12:43:51 -0800899 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700900 if (ipv4Dst.isMulticast()) {
901 if (ipv4Dst.prefixLength() != 32) {
902 log.warn("Multicast specific forwarding objective can only be /32");
903 fail(fwd, ObjectiveError.BADPARAMS);
904 return ImmutableSet.of();
905 }
906 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
907 if (assignedVlan == null) {
908 log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
909 fail(fwd, ObjectiveError.BADPARAMS);
910 return ImmutableSet.of();
911 }
912 filteredSelector.matchVlanId(assignedVlan);
913 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
914 forTableId = MULTICAST_ROUTING_TABLE;
915 log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
916 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800917 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700918 if (ipv4Dst.prefixLength() == 0) {
919 // The entire IPV4_DST field is wildcarded intentionally
920 filteredSelector.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700921 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700922 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700923 }
924 forTableId = UNICAST_ROUTING_TABLE;
925 log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
926 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800927 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800928 } else if (ethType.ethType().toShort() == Ethernet.TYPE_IPV6) {
Julia Ferguson65428c32017-08-10 18:15:24 +0000929 IpPrefix ipv6Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV6_DST)).ip();
930 if (ipv6Dst.isMulticast()) {
931 if (ipv6Dst.prefixLength() != IpAddress.INET6_BIT_LENGTH) {
932 log.debug("Multicast specific IPv6 forwarding objective can only be /128");
933 fail(fwd, ObjectiveError.BADPARAMS);
934 return ImmutableSet.of();
935 }
936 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
937 if (assignedVlan == null) {
938 log.debug("VLAN ID required by multicast specific fwd obj is missing. Abort.");
939 fail(fwd, ObjectiveError.BADPARAMS);
940 return ImmutableSet.of();
941 }
942 filteredSelector.matchVlanId(assignedVlan);
943 filteredSelector.matchEthType(Ethernet.TYPE_IPV6).matchIPv6Dst(ipv6Dst);
944 forTableId = MULTICAST_ROUTING_TABLE;
945 log.debug("processing IPv6 multicast specific forwarding objective {} -> next:{}"
946 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
947 } else {
948 if (buildIpv6Selector(filteredSelector, fwd) < 0) {
949 return Collections.emptyList();
950 }
951 forTableId = UNICAST_ROUTING_TABLE;
Pier Ventree0ae7a32016-11-23 09:57:42 -0800952 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800953 } else {
954 filteredSelector
955 .matchEthType(Ethernet.MPLS_UNICAST)
956 .matchMplsLabel(((MplsCriterion)
957 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
958 MplsBosCriterion bos = (MplsBosCriterion) selector
959 .getCriterion(Criterion.Type.MPLS_BOS);
960 if (bos != null) {
961 filteredSelector.matchMplsBos(bos.mplsBos());
962 }
963 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800964 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
965 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800966 }
967
968 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
969 if (fwd.treatment() != null) {
970 for (Instruction i : fwd.treatment().allInstructions()) {
Charles Chanab591602019-01-22 17:25:04 -0800971 if (i instanceof L3ModificationInstruction) {
Charles Chan40132b32017-01-22 00:19:37 -0800972 L3ModificationInstruction l3instr = (L3ModificationInstruction) i;
973 if (l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_IN) ||
974 l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_OUT)) {
975 continue;
976 }
977 }
Charles Chan7d10b162015-12-07 18:54:45 -0800978 /*
979 * NOTE: OF-DPA does not support immediate instruction in
980 * L3 unicast and MPLS table.
981 */
982 tb.deferred().add(i);
Saurav Das8a0732e2015-11-20 15:27:53 -0800983 }
984 }
985
986 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800987 NextGroup next = getGroupForNextObjective(fwd.nextId());
988 if (next != null) {
989 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
990 // we only need the top level group's key to point the flow to it
991 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
992 if (group == null) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700993 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
994 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800995 fail(fwd, ObjectiveError.GROUPMISSING);
996 return Collections.emptySet();
997 }
998 tb.deferred().group(group.id());
Saurav Das8a0732e2015-11-20 15:27:53 -0800999 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001000 }
1001 tb.transition(ACL_TABLE);
1002 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
1003 .fromApp(fwd.appId())
1004 .withPriority(fwd.priority())
1005 .forDevice(deviceId)
1006 .withSelector(filteredSelector.build())
1007 .withTreatment(tb.build())
1008 .forTable(forTableId);
1009
1010 if (fwd.permanent()) {
1011 ruleBuilder.makePermanent();
1012 } else {
1013 ruleBuilder.makeTemporary(fwd.timeout());
1014 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001015 Collection<FlowRule> flowRuleCollection = new ArrayList<>();
1016 flowRuleCollection.add(ruleBuilder.build());
1017 if (defaultRule) {
Pier Ventree0ae7a32016-11-23 09:57:42 -08001018 flowRuleCollection.add(
1019 defaultRoute(fwd, complementarySelector, forTableId, tb)
1020 );
Flavio Castroe10fa242016-01-15 12:43:51 -08001021 log.debug("Default rule 0.0.0.0/0 is being installed two rules");
1022 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001023 return flowRuleCollection;
Saurav Das8a0732e2015-11-20 15:27:53 -08001024 }
1025
Charles Chan1e492d32016-01-30 23:22:37 -08001026 @Override
1027 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
1028 List<FlowRule> rules = new ArrayList<>();
1029
1030 // Build filtered selector
1031 TrafficSelector selector = fwd.selector();
1032 EthCriterion ethCriterion = (EthCriterion) selector
1033 .getCriterion(Criterion.Type.ETH_DST);
1034 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
Andrea Campanella7c977b92018-05-30 21:39:45 -07001035 .getCriterion(VLAN_VID);
Charles Chan1e492d32016-01-30 23:22:37 -08001036
1037 if (vlanIdCriterion == null) {
1038 log.warn("Forwarding objective for bridging requires vlan. Not "
1039 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
1040 fail(fwd, ObjectiveError.BADPARAMS);
1041 return Collections.emptySet();
1042 }
1043
1044 TrafficSelector.Builder filteredSelectorBuilder =
1045 DefaultTrafficSelector.builder();
1046 // Do not match MacAddress for subnet broadcast entry
Charles Chand05f54b2017-02-13 11:56:54 -08001047 if (!ethCriterion.mac().equals(NONE) && !ethCriterion.mac().equals(BROADCAST)) {
Charles Chan1e492d32016-01-30 23:22:37 -08001048 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
1049 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
1050 fwd.id(), fwd.nextId(), deviceId);
1051 } else {
1052 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
1053 + "in dev:{} for vlan:{}",
1054 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
1055 }
1056 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
1057 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
1058
1059 if (fwd.treatment() != null) {
1060 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
1061 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
1062 }
1063
1064 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1065 if (fwd.nextId() != null) {
1066 NextGroup next = getGroupForNextObjective(fwd.nextId());
1067 if (next != null) {
1068 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1069 // we only need the top level group's key to point the flow to it
1070 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1071 if (group != null) {
1072 treatmentBuilder.deferred().group(group.id());
1073 } else {
1074 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1075 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
1076 fail(fwd, ObjectiveError.GROUPMISSING);
1077 return Collections.emptySet();
1078 }
1079 }
1080 }
1081 treatmentBuilder.immediate().transition(ACL_TABLE);
1082 TrafficTreatment filteredTreatment = treatmentBuilder.build();
1083
1084 // Build bridging table entries
1085 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
1086 flowRuleBuilder.fromApp(fwd.appId())
1087 .withPriority(fwd.priority())
1088 .forDevice(deviceId)
1089 .withSelector(filteredSelector)
1090 .withTreatment(filteredTreatment)
1091 .forTable(BRIDGING_TABLE);
1092 if (fwd.permanent()) {
1093 flowRuleBuilder.makePermanent();
1094 } else {
1095 flowRuleBuilder.makeTemporary(fwd.timeout());
1096 }
1097 rules.add(flowRuleBuilder.build());
1098 return rules;
1099 }
1100
Saurav Das52025962016-01-28 22:30:01 -08001101 @Override
Charles Chanab591602019-01-22 17:25:04 -08001102 protected TrafficTreatment.Builder versatileTreatmentBuilder(ForwardingObjective fwd) {
Saurav Das52025962016-01-28 22:30:01 -08001103 // XXX driver does not currently do type checking as per Tables 65-67 in
1104 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
1105 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
1106 if (fwd.treatment() != null) {
1107 for (Instruction ins : fwd.treatment().allInstructions()) {
1108 if (ins instanceof OutputInstruction) {
1109 OutputInstruction o = (OutputInstruction) ins;
Charles Chanab591602019-01-22 17:25:04 -08001110 if (PortNumber.CONTROLLER.equals(o.port())) {
Charles Chan0f43e472017-02-14 14:00:16 -08001111 ttBuilder.transition(PUNT_TABLE);
Saurav Das52025962016-01-28 22:30:01 -08001112 } else {
1113 log.warn("Only allowed treatments in versatile forwarding "
1114 + "objectives are punts to the controller");
1115 }
Charles Chanf76de302018-06-15 18:54:18 -07001116 } else if (ins instanceof NoActionInstruction) {
1117 // No action is allowed and nothing needs to be done
Saurav Das52025962016-01-28 22:30:01 -08001118 } else {
1119 log.warn("Cannot process instruction in versatile fwd {}", ins);
1120 }
1121 }
Charles Chan2df0e8a2017-01-09 11:45:08 -08001122 if (fwd.treatment().clearedDeferred()) {
1123 ttBuilder.wipeDeferred();
1124 }
Saurav Das52025962016-01-28 22:30:01 -08001125 }
1126 if (fwd.nextId() != null) {
Charles Chanab591602019-01-22 17:25:04 -08001127 // Override case
Saurav Das52025962016-01-28 22:30:01 -08001128 NextGroup next = getGroupForNextObjective(fwd.nextId());
Charles Chanab591602019-01-22 17:25:04 -08001129 if (next == null) {
1130 fail(fwd, ObjectiveError.BADPARAMS);
1131 return null;
1132 }
Saurav Das52025962016-01-28 22:30:01 -08001133 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1134 // we only need the top level group's key to point the flow to it
1135 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1136 if (group == null) {
1137 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
Charles Chanab591602019-01-22 17:25:04 -08001138 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das52025962016-01-28 22:30:01 -08001139 fail(fwd, ObjectiveError.GROUPMISSING);
Charles Chanab591602019-01-22 17:25:04 -08001140 return null;
Saurav Das52025962016-01-28 22:30:01 -08001141 }
1142 ttBuilder.deferred().group(group.id());
1143 }
Charles Chanab591602019-01-22 17:25:04 -08001144 return ttBuilder;
Saurav Das52025962016-01-28 22:30:01 -08001145 }
1146
1147 /*
Charles Chanab591602019-01-22 17:25:04 -08001148 * Open vSwitch emulation requires table-miss-entries in forwarding tables.
Saurav Das52025962016-01-28 22:30:01 -08001149 * Real OFDPA does not require these rules as they are put in by default.
1150 *
1151 * (non-Javadoc)
1152 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#initializePipeline()
1153 */
Saurav Das2857f382015-11-03 14:39:27 -08001154 @Override
Saurav Das558afec2015-05-31 17:12:48 -07001155 protected void initializePipeline() {
Charles Chanf6ec1532017-02-08 16:10:40 -08001156 initTableMiss(PORT_TABLE, VLAN_TABLE, null);
1157 initTableMiss(VLAN_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001158 initTableMiss(VLAN_1_TABLE, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001159 initTableMiss(TMAC_TABLE, BRIDGING_TABLE, null);
1160 initTableMiss(UNICAST_ROUTING_TABLE, ACL_TABLE, null);
1161 initTableMiss(MULTICAST_ROUTING_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001162 initTableMiss(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS, ACL_TABLE, null);
1163 initTableMiss(UNICAST_ROUTING_TABLE_1, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001164 initTableMiss(MPLS_TABLE_0, MPLS_TABLE_1, null);
1165 initTableMiss(MPLS_TABLE_1, ACL_TABLE, null);
1166 initTableMiss(BRIDGING_TABLE, ACL_TABLE, null);
1167 initTableMiss(ACL_TABLE, -1, null);
Charles Chan7f9d8462019-10-17 20:09:45 -07001168 initPuntTable();
Charles Chan0f43e472017-02-14 14:00:16 -08001169
Charles Chanab591602019-01-22 17:25:04 -08001170 initPopVlanPuntGroup();
Saurav Das558afec2015-05-31 17:12:48 -07001171 }
1172
Charles Chanf6ec1532017-02-08 16:10:40 -08001173 /**
1174 * Install table-miss flow entry.
1175 *
1176 * If treatment exists, use it directly.
1177 * Else if treatment does not exist but nextTable > 0, transit to next table.
1178 * Else apply empty treatment.
1179 *
1180 * @param thisTable this table ID
1181 * @param nextTable next table ID
1182 * @param treatment traffic treatment to apply.
1183 */
1184 private void initTableMiss(int thisTable, int nextTable, TrafficTreatment treatment) {
Saurav Das558afec2015-05-31 17:12:48 -07001185 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chanf6ec1532017-02-08 16:10:40 -08001186 TrafficSelector selector = DefaultTrafficSelector.builder().build();
Saurav Das558afec2015-05-31 17:12:48 -07001187
Charles Chanf6ec1532017-02-08 16:10:40 -08001188 if (treatment == null) {
1189 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1190 if (nextTable > 0) {
1191 tBuilder.transition(nextTable);
Saurav Das558afec2015-05-31 17:12:48 -07001192 }
Charles Chanf6ec1532017-02-08 16:10:40 -08001193 treatment = tBuilder.build();
1194 }
Saurav Das558afec2015-05-31 17:12:48 -07001195
Charles Chanb7504392017-02-10 12:51:04 -08001196 FlowRule rule = DefaultFlowRule.builder()
1197 .forDevice(deviceId)
Charles Chanf6ec1532017-02-08 16:10:40 -08001198 .withSelector(selector)
1199 .withTreatment(treatment)
Charles Chanb7504392017-02-10 12:51:04 -08001200 .withPriority(LOWEST_PRIORITY)
1201 .fromApp(driverId)
1202 .makePermanent()
Charles Chanf6ec1532017-02-08 16:10:40 -08001203 .forTable(thisTable).build();
Charles Chanb7504392017-02-10 12:51:04 -08001204 ops = ops.add(rule);
Saurav Das2857f382015-11-03 14:39:27 -08001205
1206 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1207 @Override
1208 public void onSuccess(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001209 log.info("Initialized table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001210 }
Saurav Das2857f382015-11-03 14:39:27 -08001211 @Override
1212 public void onError(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001213 log.warn("Failed to initialize table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001214 }
1215 }));
1216 }
Charles Chan0f43e472017-02-14 14:00:16 -08001217
1218 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001219 * Install lldp/bbdp matching rules at table PUNT_TABLE
1220 * that forward traffic to controller.
1221 *
1222 */
Charles Chan7f9d8462019-10-17 20:09:45 -07001223 private void initPuntTable() {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001224 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1225 TrafficTreatment treatment = DefaultTrafficTreatment.builder().punt().build();
1226
Charles Chan7f9d8462019-10-17 20:09:45 -07001227 // Add punt rule for LLDP and BDDP
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001228 TrafficSelector.Builder lldpSelector = DefaultTrafficSelector.builder();
1229 lldpSelector.matchEthType(EthType.EtherType.LLDP.ethType().toShort());
1230 FlowRule lldpRule = DefaultFlowRule.builder()
1231 .forDevice(deviceId)
1232 .withSelector(lldpSelector.build())
1233 .withTreatment(treatment)
1234 .withPriority(HIGHEST_PRIORITY)
1235 .fromApp(driverId)
1236 .makePermanent()
1237 .forTable(PUNT_TABLE).build();
1238 ops = ops.add(lldpRule);
1239
1240 TrafficSelector.Builder bbdpSelector = DefaultTrafficSelector.builder();
1241 bbdpSelector.matchEthType(EthType.EtherType.BDDP.ethType().toShort());
1242 FlowRule bbdpRule = DefaultFlowRule.builder()
1243 .forDevice(deviceId)
1244 .withSelector(bbdpSelector.build())
1245 .withTreatment(treatment)
1246 .withPriority(HIGHEST_PRIORITY)
1247 .fromApp(driverId)
1248 .makePermanent()
1249 .forTable(PUNT_TABLE).build();
1250 ops.add(bbdpRule);
1251
1252 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1253 @Override
1254 public void onSuccess(FlowRuleOperations ops) {
Charles Chan7f9d8462019-10-17 20:09:45 -07001255 log.info("Initialized table {} on {}", PUNT_TABLE, deviceId);
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001256 }
1257 @Override
1258 public void onError(FlowRuleOperations ops) {
Charles Chan7f9d8462019-10-17 20:09:45 -07001259 log.warn("Failed to initialize table {} on {}", PUNT_TABLE, deviceId);
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001260 }
1261 }));
1262 }
1263
1264 /**
Charles Chan0f43e472017-02-14 14:00:16 -08001265 * Builds a indirect group contains pop_vlan and punt actions.
1266 * <p>
1267 * Using group instead of immediate action to ensure that
1268 * the copy of packet on the data plane is not affected by the pop vlan action.
1269 */
1270 private void initPopVlanPuntGroup() {
Yi Tsengef19de12017-04-24 11:33:05 -07001271 GroupKey groupKey = popVlanPuntGroupKey();
Charles Chan0f43e472017-02-14 14:00:16 -08001272 TrafficTreatment bucketTreatment = DefaultTrafficTreatment.builder()
1273 .popVlan().punt().build();
1274 GroupBucket bucket =
1275 DefaultGroupBucket.createIndirectGroupBucket(bucketTreatment);
1276 GroupDescription groupDesc =
1277 new DefaultGroupDescription(
1278 deviceId,
1279 GroupDescription.Type.INDIRECT,
1280 new GroupBuckets(Collections.singletonList(bucket)),
1281 groupKey,
1282 POP_VLAN_PUNT_GROUP_ID,
1283 driverId);
1284 groupService.addGroup(groupDesc);
1285
1286 log.info("Initialized pop vlan punt group on {}", deviceId);
1287 }
Yi Tsengef19de12017-04-24 11:33:05 -07001288
1289 /**
1290 * Generates group key for a static indirect group that pop vlan and punt to
1291 * controller.
1292 *
1293 * @return the group key of the indirect table
1294 */
1295 private GroupKey popVlanPuntGroupKey() {
Charles Chan367c1c12018-10-19 16:23:28 -07001296 int hash = POP_VLAN_PUNT_GROUP_ID | (Objects.hash(deviceId) & FOUR_NIBBLE_MASK);
Yi Tsengef19de12017-04-24 11:33:05 -07001297 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
1298 }
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001299
1300 private class PopVlanPuntGroupChecker implements Runnable {
1301 @Override
1302 public void run() {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001303 try {
1304 groupCheckerLock.lock();
1305 // this can happen outside of the lock but I think it is safer
1306 // to include it here.
1307 Group group = groupService.getGroup(deviceId, popVlanPuntGroupKey());
Charles Chan4a288d92019-02-16 00:11:18 +00001308 if (group != null) {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001309 log.debug("PopVlanPuntGroupChecker: Installing {} missing rules at punt table.",
1310 flowRuleQueue.size());
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001311
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001312 // if we have pending flow rules install them
1313 if (flowRuleQueue.size() > 0) {
1314 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1315 // we should not care about the context here, it can only be add
1316 // since when removing the rules the group should be there already.
1317 flowRuleQueue.forEach(ops::add);
1318 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1319 @Override
1320 public void onSuccess(FlowRuleOperations ops) {
1321 log.debug("Applied {} pop vlan punt rules in device {}",
1322 ops.stages().get(0).size(), deviceId);
1323 }
1324
1325 @Override
1326 public void onError(FlowRuleOperations ops) {
1327 log.error("Failed to apply all pop vlan punt rules in dev {}", deviceId);
1328 }
1329 }));
1330 }
1331 // this signifies that the group is created and now
1332 // flow rules can be installed directly
1333 flowRuleQueue = null;
pier02a331a2020-01-23 11:59:56 +01001334 // Schedule with an initial delay the miss table flow rule installation
1335 // the delay is to make sure the queued flows are all installed before
1336 // pushing the table miss flow rule
1337 // TODO it can be further optimized by using context and completable future
1338 groupChecker.schedule(new TableMissFlowInstaller(), 5000, TimeUnit.MILLISECONDS);
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001339 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001340 } finally {
1341 groupCheckerLock.unlock();
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001342 }
1343 }
1344 }
pier02a331a2020-01-23 11:59:56 +01001345
1346 private class TableMissFlowInstaller implements Runnable {
1347 @Override
1348 public void run() {
1349 // Add table miss flow rule
1350 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1351 TrafficSelector.Builder defaultSelector = DefaultTrafficSelector.builder();
1352 TrafficTreatment treatment = DefaultTrafficTreatment.builder().punt().build();
1353 FlowRule defaultRule = DefaultFlowRule.builder()
1354 .forDevice(deviceId)
1355 .withSelector(defaultSelector.build())
1356 .withTreatment(treatment)
1357 .withPriority(LOWEST_PRIORITY)
1358 .fromApp(driverId)
1359 .makePermanent()
1360 .forTable(PUNT_TABLE).build();
1361 ops.add(defaultRule);
1362 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1363 @Override
1364 public void onSuccess(FlowRuleOperations ops) {
1365 log.info("Initialized table miss flow rule {} on {}", PUNT_TABLE, deviceId);
1366 }
1367 @Override
1368 public void onError(FlowRuleOperations ops) {
1369 log.warn("Failed to initialize table miss flow rule {} on {}", PUNT_TABLE, deviceId);
1370 }
1371 }));
1372 // shutdown the group checker gracefully
1373 groupChecker.shutdown();
1374 }
1375 }
Saurav Das558afec2015-05-31 17:12:48 -07001376}