blob: 07e4415971069d373ad27b7575945a362ed227a7 [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) {
Charles Chanab591602019-01-22 17:25:04 -0800155 groupHandler = new OvsOfdpaGroupHandler();
Charles Chan40132b32017-01-22 00:19:37 -0800156 groupHandler.init(deviceId, context);
Charles Chan425854b2016-04-11 15:32:12 -0700157 }
158
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700159 @Override
160 public void init(DeviceId deviceId, PipelinerContext context) {
pier1b7dd122020-02-28 09:24:11 +0100161 if (!ready.getAndSet(true)) {
162 // create a new executor at each init and a new empty queue
163 groupChecker = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/driver",
164 "ovs-ofdpa-%d", log));
165 flowRuleQueue = new ConcurrentLinkedQueue<>();
166 groupCheckerLock = new ReentrantLock();
167 groupChecker.scheduleAtFixedRate(new PopVlanPuntGroupChecker(), 20, 50, TimeUnit.MILLISECONDS);
168 super.init(deviceId, context);
169 }
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700170 }
pier9469f3e2019-04-17 17:05:08 +0200171
Andrea Campanella7c977b92018-05-30 21:39:45 -0700172 protected void processFilter(FilteringObjective filteringObjective,
173 boolean install,
174 ApplicationId applicationId) {
pier9469f3e2019-04-17 17:05:08 +0200175 if (OfdpaPipelineUtility.isDoubleTagged(filteringObjective)) {
Andrea Campanella7c977b92018-05-30 21:39:45 -0700176 processDoubleTaggedFilter(filteringObjective, install, applicationId);
177 } else {
178 // If it is not a double-tagged filter, we fall back
179 // to the OFDPA 2.0 pipeline.
180 super.processFilter(filteringObjective, install, applicationId);
181 }
182 }
183
184 /**
Andrea Campanella7c977b92018-05-30 21:39:45 -0700185 * Determines if the forwarding objective will be used for double-tagged packets.
186 *
187 * @param fwd Forwarding objective
188 * @return True if the objective was created for double-tagged packets, false otherwise.
189 */
190 private boolean isDoubleTagged(ForwardingObjective fwd) {
191 if (fwd.nextId() != null) {
192 NextGroup next = getGroupForNextObjective(fwd.nextId());
193 if (next != null) {
194 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
195 // we only need the top level group's key
196 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
197 if (group != null) {
198 int groupId = group.id().id();
199 if (((groupId & ~TYPE_MASK) == L3_UNICAST_TYPE) &&
200 ((groupId & TYPE_L3UG_DOUBLE_VLAN_MASK) == TYPE_L3UG_DOUBLE_VLAN_MASK)) {
201 return true;
202 }
203 }
204 }
205 }
206 return false;
207 }
208
209 /**
210 * Configure filtering rules of outer and inner VLAN IDs, and a MAC address.
211 * Filtering happens in three tables (VLAN_TABLE, VLAN_1_TABLE, TMAC_TABLE).
212 *
213 * @param filteringObjective the filtering objective
214 * @param install true to add, false to remove
215 * @param applicationId for application programming this filter
216 */
217 private void processDoubleTaggedFilter(FilteringObjective filteringObjective,
218 boolean install,
219 ApplicationId applicationId) {
220 PortCriterion portCriterion = null;
221 EthCriterion ethCriterion = null;
222 VlanIdCriterion innervidCriterion = null;
223 VlanIdCriterion outerVidCriterion = null;
224 boolean popVlan = false;
225 TrafficTreatment meta = filteringObjective.meta();
226 if (!filteringObjective.key().equals(Criteria.dummy()) &&
227 filteringObjective.key().type() == Criterion.Type.IN_PORT) {
228 portCriterion = (PortCriterion) filteringObjective.key();
229 }
230 if (portCriterion == null) {
231 log.warn("No IN_PORT defined in filtering objective from app: {}" +
232 "Failed to program VLAN tables.", applicationId);
233 return;
234 } else {
235 log.debug("Received filtering objective for dev/port: {}/{}", deviceId,
236 portCriterion.port());
237 }
238
239 // meta should have only one instruction, popVlan.
240 if (meta != null && meta.allInstructions().size() == 1) {
241 L2ModificationInstruction l2Inst = (L2ModificationInstruction) meta.allInstructions().get(0);
242 if (l2Inst.subtype().equals(L2ModificationInstruction.L2SubType.VLAN_POP)) {
243 popVlan = true;
244 } else {
245 log.warn("Filtering objective can have only VLAN_POP instruction.");
246 return;
247 }
248 } else {
249 log.warn("Filtering objective should have one instruction.");
250 return;
251 }
252
253 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
254 for (Criterion criterion : filteringObjective.conditions()) {
255 switch (criterion.type()) {
256 case ETH_DST:
257 case ETH_DST_MASKED:
258 ethCriterion = (EthCriterion) criterion;
259 break;
260 case VLAN_VID:
Daniele Morofa382c22019-07-12 17:58:54 -0700261 outerVidCriterion = (VlanIdCriterion) criterion;
262 break;
263 case INNER_VLAN_VID:
264 innervidCriterion = (VlanIdCriterion) criterion;
Andrea Campanella7c977b92018-05-30 21:39:45 -0700265 break;
266 default:
267 log.warn("Unsupported filter {}", criterion);
268 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
269 return;
270 }
271 }
272
273 if (innervidCriterion == null || outerVidCriterion == null) {
274 log.warn("filtering objective should have two vidCriterion.");
275 return;
276 }
277
278 if (ethCriterion == null || ethCriterion.mac().equals(NONE)) {
279 // NOTE: it is possible that a filtering objective only has vidCriterion
280 log.warn("filtering objective missing dstMac, cannot program TMAC table");
281 return;
282 } else {
283 MacAddress unicastMac = readEthDstFromTreatment(filteringObjective.meta());
284 List<List<FlowRule>> allStages = processEthDstFilter(portCriterion, ethCriterion, innervidCriterion,
285 innervidCriterion.vlanId(), unicastMac,
286 applicationId);
287 for (List<FlowRule> flowRules : allStages) {
288 log.trace("Starting a new flow rule stage for TMAC table flow");
289 ops.newStage();
290
291 for (FlowRule flowRule : flowRules) {
292 log.trace("{} flow rules in TMAC table: {} for dev: {}",
293 (install) ? "adding" : "removing", flowRules, deviceId);
294 if (install) {
295 ops = ops.add(flowRule);
296 } else {
297 // NOTE: Only remove TMAC flow when there is no more enabled port within the
298 // same VLAN on this device if TMAC doesn't support matching on in_port.
299 if (matchInPortTmacTable()
300 || (filteringObjective.meta() != null
301 && filteringObjective.meta().clearedDeferred())) {
302 ops = ops.remove(flowRule);
303 } else {
304 log.debug("Abort TMAC flow removal on {}. Some other ports still share this TMAC flow");
305 }
306 }
307 }
308 }
309 }
310
311 List<FlowRule> rules;
312 rules = processDoubleVlanIdFilter(portCriterion, innervidCriterion,
313 outerVidCriterion, popVlan, applicationId);
314 for (FlowRule flowRule : rules) {
315 log.trace("{} flow rule in VLAN table: {} for dev: {}",
316 (install) ? "adding" : "removing", flowRule, deviceId);
317 ops = install ? ops.add(flowRule) : ops.remove(flowRule);
318 }
319
320 // apply filtering flow rules
321 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
322 @Override
323 public void onSuccess(FlowRuleOperations ops) {
324 log.debug("Applied {} filtering rules in device {}",
325 ops.stages().get(0).size(), deviceId);
326 pass(filteringObjective);
327 }
328
329 @Override
330 public void onError(FlowRuleOperations ops) {
331 log.info("Failed to apply all filtering rules in dev {}", deviceId);
332 fail(filteringObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
333 }
334 }));
335
336 }
337 /**
338 * Internal implementation of processDoubleVlanIdFilter.
339 *
340 * @param portCriterion port on device for which this filter is programmed
341 * @param innerVidCriterion inner vlan
342 * @param outerVidCriterion outer vlan
343 * @param popVlan true if outer vlan header needs to be removed
344 * @param applicationId for application programming this filter
345 * @return flow rules for port-vlan filters
346 */
347 private List<FlowRule> processDoubleVlanIdFilter(PortCriterion portCriterion,
348 VlanIdCriterion innerVidCriterion,
349 VlanIdCriterion outerVidCriterion,
350 boolean popVlan,
351 ApplicationId applicationId) {
352 List<FlowRule> rules = new ArrayList<>();
353 TrafficSelector.Builder outerSelector = DefaultTrafficSelector.builder();
354 TrafficTreatment.Builder outerTreatment = DefaultTrafficTreatment.builder();
355 TrafficSelector.Builder innerSelector = DefaultTrafficSelector.builder();
356 TrafficTreatment.Builder innerTreatment = DefaultTrafficTreatment.builder();
357
358 VlanId outerVlanId = outerVidCriterion.vlanId();
359 VlanId innerVlanId = innerVidCriterion.vlanId();
360 PortNumber portNumber = portCriterion.port();
361 // Check arguments
362 if (PortNumber.ALL.equals(portNumber)
363 || outerVlanId.equals(VlanId.NONE)
364 || innerVlanId.equals(VlanId.NONE)) {
365 log.warn("Incomplete Filtering Objective. " +
366 "VLAN Table cannot be programmed for {}", deviceId);
367 return ImmutableList.of();
368 } else {
369 outerSelector.matchInPort(portNumber);
370 innerSelector.matchInPort(portNumber);
371 outerTreatment.transition(VLAN_1_TABLE);
372 innerTreatment.transition(TMAC_TABLE);
373 outerTreatment.writeMetadata(outerVlanId.toShort(), 0xFFF);
374
375 outerSelector.matchVlanId(outerVlanId);
376 innerSelector.matchVlanId(innerVlanId);
377 //force recompilation
378 //FIXME might be issue due tu /fff mask
379 innerSelector.matchMetadata(outerVlanId.toShort());
380
381 if (popVlan) {
382 outerTreatment.popVlan();
383 }
384 }
385
386 // NOTE: for double-tagged packets, restore original outer vlan
387 // before sending it to the controller.
Charles Chanab591602019-01-22 17:25:04 -0800388 GroupKey groupKey = popVlanPuntGroupKey();
389 Group group = groupService.getGroup(deviceId, groupKey);
390 if (group != null) {
391 // push outer vlan and send to controller
392 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
393 .matchInPort(portNumber)
394 .matchVlanId(innerVlanId);
395 Host host = handler().get(HostService.class).getConnectedHosts(ConnectPoint.
396 deviceConnectPoint(deviceId + "/" + portNumber.toLong())).stream().filter(h ->
397 h.vlan().equals(outerVlanId)).findFirst().orElse(null);
398 EthType vlanType = EthType.EtherType.VLAN.ethType();
399 if (host != null) {
400 vlanType = host.tpid();
Andrea Campanella7c977b92018-05-30 21:39:45 -0700401 }
Charles Chanab591602019-01-22 17:25:04 -0800402 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
403 .pushVlan(vlanType).setVlanId(outerVlanId).punt();
404
405 rules.add(DefaultFlowRule.builder()
406 .forDevice(deviceId)
407 .withSelector(sbuilder.build())
408 .withTreatment(tbuilder.build())
409 .withPriority(PacketPriority.CONTROL.priorityValue())
410 .fromApp(driverId)
411 .makePermanent()
412 .forTable(PUNT_TABLE).build());
413 } else {
414 log.info("popVlanPuntGroup not found in dev:{}", deviceId);
415 return Collections.emptyList();
Andrea Campanella7c977b92018-05-30 21:39:45 -0700416 }
Charles Chanab591602019-01-22 17:25:04 -0800417
Andrea Campanella7c977b92018-05-30 21:39:45 -0700418 FlowRule outerRule = DefaultFlowRule.builder()
419 .forDevice(deviceId)
420 .withSelector(outerSelector.build())
421 .withTreatment(outerTreatment.build())
422 .withPriority(DEFAULT_PRIORITY)
423 .fromApp(applicationId)
424 .makePermanent()
425 .forTable(VLAN_TABLE)
426 .build();
427 FlowRule innerRule = DefaultFlowRule.builder()
428 .forDevice(deviceId)
429 .withSelector(innerSelector.build())
430 .withTreatment(innerTreatment.build())
431 .withPriority(DEFAULT_PRIORITY)
432 .fromApp(applicationId)
433 .makePermanent()
434 .forTable(VLAN_1_TABLE)
435 .build();
436 rules.add(outerRule);
437 rules.add(innerRule);
438
439 return rules;
440 }
441
442 /**
443 * In the OF-DPA 2.0 pipeline, egress forwarding objectives go to the
444 * egress tables.
445 * @param fwd the forwarding objective of type 'egress'
446 * @return a collection of flow rules to be sent to the switch. An empty
447 * collection may be returned if there is a problem in processing
448 * the flow rule
449 */
450 @Override
451 protected Collection<FlowRule> processEgress(ForwardingObjective fwd) {
452 log.debug("Processing egress forwarding objective:{} in dev:{}",
453 fwd, deviceId);
454
455 List<FlowRule> rules = new ArrayList<>();
456
457 // Build selector
458 TrafficSelector.Builder sb = DefaultTrafficSelector.builder();
459 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) fwd.selector().getCriterion(Criterion.Type.VLAN_VID);
460 if (vlanIdCriterion == null) {
461 log.error("Egress forwarding objective:{} must include vlanId", fwd.id());
462 fail(fwd, ObjectiveError.BADPARAMS);
463 return rules;
464 }
465
466 Optional<Instruction> outInstr = fwd.treatment().allInstructions().stream()
467 .filter(instruction -> instruction instanceof Instructions.OutputInstruction).findFirst();
468 if (!outInstr.isPresent()) {
469 log.error("Egress forwarding objective:{} must include output port", fwd.id());
470 fail(fwd, ObjectiveError.BADPARAMS);
471 return rules;
472 }
473
474 PortNumber portNumber = ((Instructions.OutputInstruction) outInstr.get()).port();
475
476 sb.matchVlanId(vlanIdCriterion.vlanId());
477 OfdpaMatchActsetOutput actsetOutput = new OfdpaMatchActsetOutput(portNumber);
478 sb.extension(actsetOutput, deviceId);
479
480 // Build a flow rule for Egress VLAN Flow table
481 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
482 tb.transition(UNICAST_ROUTING_TABLE_1);
483 if (fwd.treatment() != null) {
484 for (Instruction instr : fwd.treatment().allInstructions()) {
485 if (instr instanceof L2ModificationInstruction &&
486 ((L2ModificationInstruction) instr).subtype() ==
487 L2ModificationInstruction.L2SubType.VLAN_ID) {
488 tb.immediate().add(instr);
489 }
490 if (instr instanceof L2ModificationInstruction &&
491 ((L2ModificationInstruction) instr).subtype() ==
492 L2ModificationInstruction.L2SubType.VLAN_PUSH) {
493 EthType ethType = ((L2ModificationInstruction.ModVlanHeaderInstruction) instr).ethernetType();
494 tb.immediate().pushVlan(ethType);
495 }
496 }
497 }
498
499 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
500 .fromApp(fwd.appId())
501 .withPriority(fwd.priority())
502 .forDevice(deviceId)
503 .withSelector(sb.build())
504 .withTreatment(tb.build())
505 .makePermanent()
506 .forTable(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
507 rules.add(ruleBuilder.build());
508 return rules;
509 }
510
511 /**
512 * Handles forwarding rules to the IP Unicast Routing.
513 *
514 * @param fwd the forwarding objective
515 * @return A collection of flow rules, or an empty set
516 */
517 protected Collection<FlowRule> processDoubleTaggedFwd(ForwardingObjective fwd) {
518 // inner for UNICAST_ROUTING_TABLE_1, outer for UNICAST_ROUTING_TABLE
519 TrafficSelector selector = fwd.selector();
520 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
521 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
522 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
523
524 EthTypeCriterion ethType =
525 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
526
527 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
528 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
529 sBuilder.matchVlanId(VlanId.ANY);
530 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
531 if (!ipv4Dst.isMulticast() && ipv4Dst.prefixLength() == 32) {
532 sBuilder.matchIPDst(ipv4Dst);
533 if (fwd.nextId() != null) {
534 NextGroup next = getGroupForNextObjective(fwd.nextId());
535 if (next != null) {
536 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
537 // we only need the top level group's key to point the flow to it
538 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
539 if (group == null) {
540 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
541 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
542 fail(fwd, ObjectiveError.GROUPMISSING);
543 return Collections.emptySet();
544 }
545 outerTtb.immediate().setVlanId(extractDummyVlanIdFromGroupId(group.id().id()));
546 //ACTSET_OUTPUT in OVS will match output action in write_action() set.
547 outerTtb.deferred().setOutput(extractOutputPortFromGroupId(group.id().id()));
548 outerTtb.transition(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
549 innerTtb.deferred().group(group.id());
550 innerTtb.transition(ACL_TABLE);
551
552 FlowRule.Builder innerRuleBuilder = DefaultFlowRule.builder()
553 .fromApp(fwd.appId())
554 .withPriority(fwd.priority())
555 .forDevice(deviceId)
556 .withSelector(sBuilder.build())
557 .withTreatment(innerTtb.build())
558 .forTable(UNICAST_ROUTING_TABLE_1);
559 if (fwd.permanent()) {
560 innerRuleBuilder.makePermanent();
561 } else {
562 innerRuleBuilder.makeTemporary(fwd.timeout());
563 }
564 Collection<FlowRule> flowRuleCollection = new HashSet<>();
565 flowRuleCollection.add(innerRuleBuilder.build());
566
567 FlowRule.Builder outerRuleBuilder = DefaultFlowRule.builder()
568 .fromApp(fwd.appId())
569 .withPriority(fwd.priority())
570 .forDevice(deviceId)
571 .withSelector(sBuilder.build())
572 .withTreatment(outerTtb.build())
573 .forTable(UNICAST_ROUTING_TABLE);
574 if (fwd.permanent()) {
575 outerRuleBuilder.makePermanent();
576 } else {
577 outerRuleBuilder.makeTemporary(fwd.timeout());
578 }
579 flowRuleCollection.add(innerRuleBuilder.build());
580 flowRuleCollection.add(outerRuleBuilder.build());
581 return flowRuleCollection;
582 } else {
583 log.warn("Cannot find group for nextId:{} in dev:{}. Aborting fwd:{}",
584 fwd.nextId(), deviceId, fwd.id());
585 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
586 return Collections.emptySet();
587 }
588 } else {
589 log.warn("NextId is not specified in fwd:{}", fwd.id());
590 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
591 return Collections.emptySet();
592 }
593 }
594 }
595 return Collections.emptySet();
596 }
597
598 private static VlanId extractDummyVlanIdFromGroupId(int groupId) {
599 short vlanId = (short) ((groupId & 0x7FF8000) >> 15);
600 return VlanId.vlanId(vlanId);
601 }
602
603 private static PortNumber extractOutputPortFromGroupId(int groupId) {
604 return PortNumber.portNumber(groupId & 0x7FFF);
605 }
606
Saurav Das4ce45962015-11-24 23:21:05 -0800607 /*
Charles Chanab591602019-01-22 17:25:04 -0800608 * Open vSwitch emulation does not require the non OF-standard rules for
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700609 * matching untagged packets that ofdpa uses.
Saurav Das4ce45962015-11-24 23:21:05 -0800610 *
611 * (non-Javadoc)
612 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter
613 */
Saurav Das558afec2015-05-31 17:12:48 -0700614 @Override
Charles Chan66291502018-03-02 16:43:28 -0800615 protected List<List<FlowRule>> processVlanIdFilter(PortCriterion portCriterion,
pier6aef5b72019-06-10 17:10:26 +0200616 VlanIdCriterion vidCriterion,
617 VlanId assignedVlan,
618 ApplicationId applicationId,
619 boolean install) {
Charles Chan79769232016-07-05 16:34:39 -0700620 List<FlowRule> rules = new ArrayList<>();
Saurav Das2857f382015-11-03 14:39:27 -0800621 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
622 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
623 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800624 treatment.transition(TMAC_TABLE);
625
Saurav Das2857f382015-11-03 14:39:27 -0800626 if (vidCriterion.vlanId() == VlanId.NONE) {
627 // untagged packets are assigned vlans
628 treatment.pushVlan().setVlanId(assignedVlan);
Piere5bff482018-03-07 11:42:50 +0100629 } else if (!vidCriterion.vlanId().equals(assignedVlan)) {
630 // Rewrite with assigned vlans
631 treatment.setVlanId(assignedVlan);
Saurav Das2857f382015-11-03 14:39:27 -0800632 }
Saurav Das2857f382015-11-03 14:39:27 -0800633
634 // ofdpa cannot match on ALL portnumber, so we need to use separate
635 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800636 List<PortNumber> portnums = new ArrayList<>();
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700637 if (portCriterion != null) {
638 if (portCriterion.port() == PortNumber.ALL) {
639 for (Port port : deviceService.getPorts(deviceId)) {
640 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
641 portnums.add(port.number());
642 }
Saurav Das2857f382015-11-03 14:39:27 -0800643 }
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700644 } else {
645 portnums.add(portCriterion.port());
Saurav Das2857f382015-11-03 14:39:27 -0800646 }
Saurav Das2857f382015-11-03 14:39:27 -0800647 }
Saurav Das4f980082015-11-05 13:39:15 -0800648
Saurav Das2857f382015-11-03 14:39:27 -0800649 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800650 // NOTE: Emulating OFDPA behavior by popping off internal assigned
651 // VLAN before sending to controller
Charles Chanab591602019-01-22 17:25:04 -0800652 if (vidCriterion.vlanId() == VlanId.NONE) {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700653 try {
654 groupCheckerLock.lock();
655 if (flowRuleQueue == null) {
656 // this means that the group has been created
657 // and that groupChecker has destroyed the queue
658 log.debug("Installing punt table rule for untagged port {} and vlan {}.",
659 pnum, assignedVlan);
660 rules.add(buildPuntTableRule(pnum, assignedVlan));
661 } else {
662 // The VLAN punt group may be held back due to device initial audit.
663 // In that case, we queue all punt table flow until the group has been created.
664 log.debug("popVlanPuntGroup not found in dev:{}, queueing this flow rule.", deviceId);
665 flowRuleQueue.add(buildPuntTableRule(pnum, assignedVlan));
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700666 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700667 } finally {
668 groupCheckerLock.unlock();
Charles Chan0f43e472017-02-14 14:00:16 -0800669 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700670 } else if (vidCriterion.vlanId() != VlanId.NONE) {
671 // for tagged ports just forward to the controller
672 log.debug("Installing punt rule for tagged port {} and vlan {}.", pnum, vidCriterion.vlanId());
673 rules.add(buildPuntTableRuleTagged(pnum, vidCriterion.vlanId()));
Charles Chan0f43e472017-02-14 14:00:16 -0800674 }
675
Saurav Das4f980082015-11-05 13:39:15 -0800676 // create rest of flowrule
Saurav Das2857f382015-11-03 14:39:27 -0800677 selector.matchInPort(pnum);
678 FlowRule rule = DefaultFlowRule.builder()
679 .forDevice(deviceId)
680 .withSelector(selector.build())
681 .withTreatment(treatment.build())
682 .withPriority(DEFAULT_PRIORITY)
683 .fromApp(applicationId)
684 .makePermanent()
685 .forTable(VLAN_TABLE).build();
686 rules.add(rule);
687 }
Charles Chanf57a8252016-06-29 19:12:37 -0700688
Charles Chan66291502018-03-02 16:43:28 -0800689 return ImmutableList.of(rules);
Saurav Das2857f382015-11-03 14:39:27 -0800690 }
691
Pier Ventree0ae7a32016-11-23 09:57:42 -0800692 /**
Charles Chan0f43e472017-02-14 14:00:16 -0800693 * Creates punt table entry that matches IN_PORT and VLAN_VID and points to
694 * a group that pop vlan and punt.
695 *
696 * @param portNumber port number
697 * @param assignedVlan internally assigned vlan id
698 * @return punt table flow rule
699 */
700 private FlowRule buildPuntTableRule(PortNumber portNumber, VlanId assignedVlan) {
701 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
702 .matchInPort(portNumber)
703 .matchVlanId(assignedVlan);
704 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
Yi Tsengfa394de2017-02-01 11:26:40 -0800705 .group(new GroupId(POP_VLAN_PUNT_GROUP_ID));
Charles Chan0f43e472017-02-14 14:00:16 -0800706
707 return DefaultFlowRule.builder()
708 .forDevice(deviceId)
709 .withSelector(sbuilder.build())
710 .withTreatment(tbuilder.build())
711 .withPriority(PacketPriority.CONTROL.priorityValue())
712 .fromApp(driverId)
713 .makePermanent()
714 .forTable(PUNT_TABLE).build();
715 }
716
717 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700718 * Creates punt table entry that matches IN_PORT and VLAN_VID and forwards
719 * packet to controller tagged.
720 *
721 * @param portNumber port number
722 * @param packetVlan vlan tag of the packet
723 * @return punt table flow rule
724 */
725 private FlowRule buildPuntTableRuleTagged(PortNumber portNumber, VlanId packetVlan) {
726 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
727 .matchInPort(portNumber)
728 .matchVlanId(packetVlan);
729 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder().punt();
730
731 return DefaultFlowRule.builder()
732 .forDevice(deviceId)
733 .withSelector(sbuilder.build())
734 .withTreatment(tbuilder.build())
735 .withPriority(PacketPriority.CONTROL.priorityValue())
736 .fromApp(driverId)
737 .makePermanent()
738 .forTable(PUNT_TABLE).build();
739 }
740
Saurav Das8a0732e2015-11-20 15:27:53 -0800741 @Override
Charles Chan66291502018-03-02 16:43:28 -0800742 protected List<List<FlowRule>> processEthDstFilter(PortCriterion portCriterion,
Saurav Das4ce45962015-11-24 23:21:05 -0800743 EthCriterion ethCriterion,
744 VlanIdCriterion vidCriterion,
745 VlanId assignedVlan,
Charles Chan45b69ab2018-03-02 15:41:41 -0800746 MacAddress unicastMac,
Saurav Das4ce45962015-11-24 23:21:05 -0800747 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800748 // Consider PortNumber.ANY as wildcard. Match ETH_DST only
749 if (portCriterion != null && portCriterion.port() == PortNumber.ANY) {
750 return processEthDstOnlyFilter(ethCriterion, applicationId);
751 }
752
Charles Chan5b9df8d2016-03-28 22:21:40 -0700753 // Multicast MAC
754 if (ethCriterion.mask() != null) {
Charles Chan45b69ab2018-03-02 15:41:41 -0800755 return processMcastEthDstFilter(ethCriterion, assignedVlan, unicastMac, applicationId);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700756 }
757
Saurav Das4ce45962015-11-24 23:21:05 -0800758 //handling untagged packets via assigned VLAN
759 if (vidCriterion.vlanId() == VlanId.NONE) {
760 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
761 }
762 // ofdpa cannot match on ALL portnumber, so we need to use separate
763 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800764 List<PortNumber> portnums = new ArrayList<>();
Ray Milkey74e59132018-01-17 15:24:52 -0800765 if (portCriterion != null) {
766 if (portCriterion.port() == PortNumber.ALL) {
767 for (Port port : deviceService.getPorts(deviceId)) {
768 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
769 portnums.add(port.number());
770 }
Saurav Das4ce45962015-11-24 23:21:05 -0800771 }
Ray Milkey74e59132018-01-17 15:24:52 -0800772 } else {
773 portnums.add(portCriterion.port());
Saurav Das4ce45962015-11-24 23:21:05 -0800774 }
Saurav Das4ce45962015-11-24 23:21:05 -0800775 }
776
Charles Chan50d900c2018-03-02 13:26:22 -0800777 List<FlowRule> rules = new ArrayList<>();
Saurav Das4ce45962015-11-24 23:21:05 -0800778 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800779 // TMAC rules for unicast IP packets
Saurav Das4ce45962015-11-24 23:21:05 -0800780 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
781 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
782 selector.matchInPort(pnum);
783 selector.matchVlanId(vidCriterion.vlanId());
784 selector.matchEthType(Ethernet.TYPE_IPV4);
785 selector.matchEthDst(ethCriterion.mac());
Saurav Das4ce45962015-11-24 23:21:05 -0800786 treatment.transition(UNICAST_ROUTING_TABLE);
787 FlowRule rule = DefaultFlowRule.builder()
788 .forDevice(deviceId)
789 .withSelector(selector.build())
790 .withTreatment(treatment.build())
791 .withPriority(DEFAULT_PRIORITY)
792 .fromApp(applicationId)
793 .makePermanent()
794 .forTable(TMAC_TABLE).build();
795 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800796
797 // TMAC rules for MPLS packets
Saurav Das4ce45962015-11-24 23:21:05 -0800798 selector = DefaultTrafficSelector.builder();
799 treatment = DefaultTrafficTreatment.builder();
800 selector.matchInPort(pnum);
801 selector.matchVlanId(vidCriterion.vlanId());
802 selector.matchEthType(Ethernet.MPLS_UNICAST);
803 selector.matchEthDst(ethCriterion.mac());
Saurav Das4ce45962015-11-24 23:21:05 -0800804 treatment.transition(MPLS_TABLE_0);
805 rule = DefaultFlowRule.builder()
806 .forDevice(deviceId)
807 .withSelector(selector.build())
808 .withTreatment(treatment.build())
809 .withPriority(DEFAULT_PRIORITY)
810 .fromApp(applicationId)
811 .makePermanent()
812 .forTable(TMAC_TABLE).build();
813 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800814
815 // TMAC rules for IPv6 packets
Pier Ventree0ae7a32016-11-23 09:57:42 -0800816 selector = DefaultTrafficSelector.builder();
817 treatment = DefaultTrafficTreatment.builder();
818 selector.matchInPort(pnum);
819 selector.matchVlanId(vidCriterion.vlanId());
820 selector.matchEthType(Ethernet.TYPE_IPV6);
821 selector.matchEthDst(ethCriterion.mac());
Pier Ventree0ae7a32016-11-23 09:57:42 -0800822 treatment.transition(UNICAST_ROUTING_TABLE);
823 rule = DefaultFlowRule.builder()
824 .forDevice(deviceId)
825 .withSelector(selector.build())
826 .withTreatment(treatment.build())
827 .withPriority(DEFAULT_PRIORITY)
828 .fromApp(applicationId)
829 .makePermanent()
830 .forTable(TMAC_TABLE).build();
831 rules.add(rule);
Saurav Das4ce45962015-11-24 23:21:05 -0800832 }
Charles Chan66291502018-03-02 16:43:28 -0800833 return ImmutableList.of(rules);
Saurav Das4ce45962015-11-24 23:21:05 -0800834 }
835
Charles Chan5270ed02016-01-30 23:22:37 -0800836 @Override
Charles Chan66291502018-03-02 16:43:28 -0800837 protected List<List<FlowRule>> processEthDstOnlyFilter(EthCriterion ethCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700838 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800839 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
840 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
841 selector.matchEthType(Ethernet.TYPE_IPV4);
842 selector.matchEthDst(ethCriterion.mac());
Charles Chan5270ed02016-01-30 23:22:37 -0800843 treatment.transition(UNICAST_ROUTING_TABLE);
844 FlowRule 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();
Charles Chan66291502018-03-02 16:43:28 -0800852 return ImmutableList.of(ImmutableList.of(rule));
Charles Chan5270ed02016-01-30 23:22:37 -0800853 }
854
Saurav Das4ce45962015-11-24 23:21:05 -0800855 /*
Charles Chanab591602019-01-22 17:25:04 -0800856 * Open vSwitch emulation allows MPLS ECMP.
Saurav Das4ce45962015-11-24 23:21:05 -0800857 *
858 * (non-Javadoc)
859 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthTypeSpecific
860 */
861 @Override
862 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
Andrea Campanella7c977b92018-05-30 21:39:45 -0700863 if (isDoubleTagged(fwd)) {
864 return processDoubleTaggedFwd(fwd);
865 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800866 TrafficSelector selector = fwd.selector();
867 EthTypeCriterion ethType =
868 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
869 if ((ethType == null) ||
870 (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
Pier Ventree0ae7a32016-11-23 09:57:42 -0800871 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST) &&
872 (ethType.ethType().toShort() != Ethernet.TYPE_IPV6)) {
Saurav Das4ce45962015-11-24 23:21:05 -0800873 log.warn("processSpecific: Unsupported forwarding objective criteria"
874 + "ethType:{} in dev:{}", ethType, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800875 fail(fwd, ObjectiveError.UNSUPPORTED);
876 return Collections.emptySet();
877 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800878 boolean defaultRule = false;
Charles Chan50d900c2018-03-02 13:26:22 -0800879 int forTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800880 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
Flavio Castroe10fa242016-01-15 12:43:51 -0800881 TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder();
882
Saurav Das8a0732e2015-11-20 15:27:53 -0800883 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Flavio Castroe10fa242016-01-15 12:43:51 -0800884 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700885 if (ipv4Dst.isMulticast()) {
886 if (ipv4Dst.prefixLength() != 32) {
887 log.warn("Multicast specific forwarding objective can only be /32");
888 fail(fwd, ObjectiveError.BADPARAMS);
889 return ImmutableSet.of();
890 }
891 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
892 if (assignedVlan == null) {
893 log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
894 fail(fwd, ObjectiveError.BADPARAMS);
895 return ImmutableSet.of();
896 }
897 filteredSelector.matchVlanId(assignedVlan);
898 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
899 forTableId = MULTICAST_ROUTING_TABLE;
900 log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
901 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800902 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700903 if (ipv4Dst.prefixLength() == 0) {
904 // The entire IPV4_DST field is wildcarded intentionally
905 filteredSelector.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700906 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700907 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700908 }
909 forTableId = UNICAST_ROUTING_TABLE;
910 log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
911 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800912 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800913 } else if (ethType.ethType().toShort() == Ethernet.TYPE_IPV6) {
Julia Ferguson65428c32017-08-10 18:15:24 +0000914 IpPrefix ipv6Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV6_DST)).ip();
915 if (ipv6Dst.isMulticast()) {
916 if (ipv6Dst.prefixLength() != IpAddress.INET6_BIT_LENGTH) {
917 log.debug("Multicast specific IPv6 forwarding objective can only be /128");
918 fail(fwd, ObjectiveError.BADPARAMS);
919 return ImmutableSet.of();
920 }
921 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
922 if (assignedVlan == null) {
923 log.debug("VLAN ID required by multicast specific fwd obj is missing. Abort.");
924 fail(fwd, ObjectiveError.BADPARAMS);
925 return ImmutableSet.of();
926 }
927 filteredSelector.matchVlanId(assignedVlan);
928 filteredSelector.matchEthType(Ethernet.TYPE_IPV6).matchIPv6Dst(ipv6Dst);
929 forTableId = MULTICAST_ROUTING_TABLE;
930 log.debug("processing IPv6 multicast specific forwarding objective {} -> next:{}"
931 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
932 } else {
933 if (buildIpv6Selector(filteredSelector, fwd) < 0) {
934 return Collections.emptyList();
935 }
936 forTableId = UNICAST_ROUTING_TABLE;
Pier Ventree0ae7a32016-11-23 09:57:42 -0800937 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800938 } else {
939 filteredSelector
940 .matchEthType(Ethernet.MPLS_UNICAST)
941 .matchMplsLabel(((MplsCriterion)
942 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
943 MplsBosCriterion bos = (MplsBosCriterion) selector
944 .getCriterion(Criterion.Type.MPLS_BOS);
945 if (bos != null) {
946 filteredSelector.matchMplsBos(bos.mplsBos());
947 }
948 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800949 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
950 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800951 }
952
953 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
954 if (fwd.treatment() != null) {
955 for (Instruction i : fwd.treatment().allInstructions()) {
Charles Chanab591602019-01-22 17:25:04 -0800956 if (i instanceof L3ModificationInstruction) {
Charles Chan40132b32017-01-22 00:19:37 -0800957 L3ModificationInstruction l3instr = (L3ModificationInstruction) i;
958 if (l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_IN) ||
959 l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_OUT)) {
960 continue;
961 }
962 }
Charles Chan7d10b162015-12-07 18:54:45 -0800963 /*
964 * NOTE: OF-DPA does not support immediate instruction in
965 * L3 unicast and MPLS table.
966 */
967 tb.deferred().add(i);
Saurav Das8a0732e2015-11-20 15:27:53 -0800968 }
969 }
970
971 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800972 NextGroup next = getGroupForNextObjective(fwd.nextId());
973 if (next != null) {
974 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
975 // we only need the top level group's key to point the flow to it
976 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
977 if (group == null) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700978 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
979 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800980 fail(fwd, ObjectiveError.GROUPMISSING);
981 return Collections.emptySet();
982 }
983 tb.deferred().group(group.id());
Saurav Das8a0732e2015-11-20 15:27:53 -0800984 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800985 }
986 tb.transition(ACL_TABLE);
987 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
988 .fromApp(fwd.appId())
989 .withPriority(fwd.priority())
990 .forDevice(deviceId)
991 .withSelector(filteredSelector.build())
992 .withTreatment(tb.build())
993 .forTable(forTableId);
994
995 if (fwd.permanent()) {
996 ruleBuilder.makePermanent();
997 } else {
998 ruleBuilder.makeTemporary(fwd.timeout());
999 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001000 Collection<FlowRule> flowRuleCollection = new ArrayList<>();
1001 flowRuleCollection.add(ruleBuilder.build());
1002 if (defaultRule) {
Pier Ventree0ae7a32016-11-23 09:57:42 -08001003 flowRuleCollection.add(
1004 defaultRoute(fwd, complementarySelector, forTableId, tb)
1005 );
Flavio Castroe10fa242016-01-15 12:43:51 -08001006 log.debug("Default rule 0.0.0.0/0 is being installed two rules");
1007 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001008 return flowRuleCollection;
Saurav Das8a0732e2015-11-20 15:27:53 -08001009 }
1010
Charles Chan1e492d32016-01-30 23:22:37 -08001011 @Override
1012 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
1013 List<FlowRule> rules = new ArrayList<>();
1014
1015 // Build filtered selector
1016 TrafficSelector selector = fwd.selector();
1017 EthCriterion ethCriterion = (EthCriterion) selector
1018 .getCriterion(Criterion.Type.ETH_DST);
1019 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
Andrea Campanella7c977b92018-05-30 21:39:45 -07001020 .getCriterion(VLAN_VID);
Charles Chan1e492d32016-01-30 23:22:37 -08001021
1022 if (vlanIdCriterion == null) {
1023 log.warn("Forwarding objective for bridging requires vlan. Not "
1024 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
1025 fail(fwd, ObjectiveError.BADPARAMS);
1026 return Collections.emptySet();
1027 }
1028
1029 TrafficSelector.Builder filteredSelectorBuilder =
1030 DefaultTrafficSelector.builder();
1031 // Do not match MacAddress for subnet broadcast entry
Charles Chand05f54b2017-02-13 11:56:54 -08001032 if (!ethCriterion.mac().equals(NONE) && !ethCriterion.mac().equals(BROADCAST)) {
Charles Chan1e492d32016-01-30 23:22:37 -08001033 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
1034 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
1035 fwd.id(), fwd.nextId(), deviceId);
1036 } else {
1037 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
1038 + "in dev:{} for vlan:{}",
1039 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
1040 }
1041 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
1042 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
1043
1044 if (fwd.treatment() != null) {
1045 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
1046 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
1047 }
1048
1049 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1050 if (fwd.nextId() != null) {
1051 NextGroup next = getGroupForNextObjective(fwd.nextId());
1052 if (next != null) {
1053 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1054 // we only need the top level group's key to point the flow to it
1055 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1056 if (group != null) {
1057 treatmentBuilder.deferred().group(group.id());
1058 } else {
1059 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1060 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
1061 fail(fwd, ObjectiveError.GROUPMISSING);
1062 return Collections.emptySet();
1063 }
1064 }
1065 }
1066 treatmentBuilder.immediate().transition(ACL_TABLE);
1067 TrafficTreatment filteredTreatment = treatmentBuilder.build();
1068
1069 // Build bridging table entries
1070 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
1071 flowRuleBuilder.fromApp(fwd.appId())
1072 .withPriority(fwd.priority())
1073 .forDevice(deviceId)
1074 .withSelector(filteredSelector)
1075 .withTreatment(filteredTreatment)
1076 .forTable(BRIDGING_TABLE);
1077 if (fwd.permanent()) {
1078 flowRuleBuilder.makePermanent();
1079 } else {
1080 flowRuleBuilder.makeTemporary(fwd.timeout());
1081 }
1082 rules.add(flowRuleBuilder.build());
1083 return rules;
1084 }
1085
Saurav Das52025962016-01-28 22:30:01 -08001086 @Override
Charles Chanab591602019-01-22 17:25:04 -08001087 protected TrafficTreatment.Builder versatileTreatmentBuilder(ForwardingObjective fwd) {
Saurav Das52025962016-01-28 22:30:01 -08001088 // XXX driver does not currently do type checking as per Tables 65-67 in
1089 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
1090 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
1091 if (fwd.treatment() != null) {
1092 for (Instruction ins : fwd.treatment().allInstructions()) {
1093 if (ins instanceof OutputInstruction) {
1094 OutputInstruction o = (OutputInstruction) ins;
Charles Chanab591602019-01-22 17:25:04 -08001095 if (PortNumber.CONTROLLER.equals(o.port())) {
Charles Chan0f43e472017-02-14 14:00:16 -08001096 ttBuilder.transition(PUNT_TABLE);
Saurav Das52025962016-01-28 22:30:01 -08001097 } else {
1098 log.warn("Only allowed treatments in versatile forwarding "
1099 + "objectives are punts to the controller");
1100 }
Charles Chanf76de302018-06-15 18:54:18 -07001101 } else if (ins instanceof NoActionInstruction) {
1102 // No action is allowed and nothing needs to be done
Saurav Das52025962016-01-28 22:30:01 -08001103 } else {
1104 log.warn("Cannot process instruction in versatile fwd {}", ins);
1105 }
1106 }
Charles Chan2df0e8a2017-01-09 11:45:08 -08001107 if (fwd.treatment().clearedDeferred()) {
1108 ttBuilder.wipeDeferred();
1109 }
Saurav Das52025962016-01-28 22:30:01 -08001110 }
1111 if (fwd.nextId() != null) {
Charles Chanab591602019-01-22 17:25:04 -08001112 // Override case
Saurav Das52025962016-01-28 22:30:01 -08001113 NextGroup next = getGroupForNextObjective(fwd.nextId());
Charles Chanab591602019-01-22 17:25:04 -08001114 if (next == null) {
1115 fail(fwd, ObjectiveError.BADPARAMS);
1116 return null;
1117 }
Saurav Das52025962016-01-28 22:30:01 -08001118 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1119 // we only need the top level group's key to point the flow to it
1120 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1121 if (group == null) {
1122 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
Charles Chanab591602019-01-22 17:25:04 -08001123 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das52025962016-01-28 22:30:01 -08001124 fail(fwd, ObjectiveError.GROUPMISSING);
Charles Chanab591602019-01-22 17:25:04 -08001125 return null;
Saurav Das52025962016-01-28 22:30:01 -08001126 }
1127 ttBuilder.deferred().group(group.id());
1128 }
Charles Chanab591602019-01-22 17:25:04 -08001129 return ttBuilder;
Saurav Das52025962016-01-28 22:30:01 -08001130 }
1131
1132 /*
Charles Chanab591602019-01-22 17:25:04 -08001133 * Open vSwitch emulation requires table-miss-entries in forwarding tables.
Saurav Das52025962016-01-28 22:30:01 -08001134 * Real OFDPA does not require these rules as they are put in by default.
1135 *
1136 * (non-Javadoc)
1137 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#initializePipeline()
1138 */
Saurav Das2857f382015-11-03 14:39:27 -08001139 @Override
Saurav Das558afec2015-05-31 17:12:48 -07001140 protected void initializePipeline() {
Charles Chanf6ec1532017-02-08 16:10:40 -08001141 initTableMiss(PORT_TABLE, VLAN_TABLE, null);
1142 initTableMiss(VLAN_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001143 initTableMiss(VLAN_1_TABLE, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001144 initTableMiss(TMAC_TABLE, BRIDGING_TABLE, null);
1145 initTableMiss(UNICAST_ROUTING_TABLE, ACL_TABLE, null);
1146 initTableMiss(MULTICAST_ROUTING_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001147 initTableMiss(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS, ACL_TABLE, null);
1148 initTableMiss(UNICAST_ROUTING_TABLE_1, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001149 initTableMiss(MPLS_TABLE_0, MPLS_TABLE_1, null);
1150 initTableMiss(MPLS_TABLE_1, ACL_TABLE, null);
1151 initTableMiss(BRIDGING_TABLE, ACL_TABLE, null);
1152 initTableMiss(ACL_TABLE, -1, null);
Charles Chan7f9d8462019-10-17 20:09:45 -07001153 initPuntTable();
Charles Chan0f43e472017-02-14 14:00:16 -08001154
Charles Chanab591602019-01-22 17:25:04 -08001155 initPopVlanPuntGroup();
Saurav Das558afec2015-05-31 17:12:48 -07001156 }
1157
Charles Chanf6ec1532017-02-08 16:10:40 -08001158 /**
1159 * Install table-miss flow entry.
1160 *
1161 * If treatment exists, use it directly.
1162 * Else if treatment does not exist but nextTable > 0, transit to next table.
1163 * Else apply empty treatment.
1164 *
1165 * @param thisTable this table ID
1166 * @param nextTable next table ID
1167 * @param treatment traffic treatment to apply.
1168 */
1169 private void initTableMiss(int thisTable, int nextTable, TrafficTreatment treatment) {
Saurav Das558afec2015-05-31 17:12:48 -07001170 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chanf6ec1532017-02-08 16:10:40 -08001171 TrafficSelector selector = DefaultTrafficSelector.builder().build();
Saurav Das558afec2015-05-31 17:12:48 -07001172
Charles Chanf6ec1532017-02-08 16:10:40 -08001173 if (treatment == null) {
1174 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1175 if (nextTable > 0) {
1176 tBuilder.transition(nextTable);
Saurav Das558afec2015-05-31 17:12:48 -07001177 }
Charles Chanf6ec1532017-02-08 16:10:40 -08001178 treatment = tBuilder.build();
1179 }
Saurav Das558afec2015-05-31 17:12:48 -07001180
Charles Chanb7504392017-02-10 12:51:04 -08001181 FlowRule rule = DefaultFlowRule.builder()
1182 .forDevice(deviceId)
Charles Chanf6ec1532017-02-08 16:10:40 -08001183 .withSelector(selector)
1184 .withTreatment(treatment)
Charles Chanb7504392017-02-10 12:51:04 -08001185 .withPriority(LOWEST_PRIORITY)
1186 .fromApp(driverId)
1187 .makePermanent()
Charles Chanf6ec1532017-02-08 16:10:40 -08001188 .forTable(thisTable).build();
Charles Chanb7504392017-02-10 12:51:04 -08001189 ops = ops.add(rule);
Saurav Das2857f382015-11-03 14:39:27 -08001190
1191 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1192 @Override
1193 public void onSuccess(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001194 log.info("Initialized table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001195 }
Saurav Das2857f382015-11-03 14:39:27 -08001196 @Override
1197 public void onError(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001198 log.warn("Failed to initialize table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001199 }
1200 }));
1201 }
Charles Chan0f43e472017-02-14 14:00:16 -08001202
1203 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001204 * Install lldp/bbdp matching rules at table PUNT_TABLE
1205 * that forward traffic to controller.
1206 *
1207 */
Charles Chan7f9d8462019-10-17 20:09:45 -07001208 private void initPuntTable() {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001209 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1210 TrafficTreatment treatment = DefaultTrafficTreatment.builder().punt().build();
1211
Charles Chan7f9d8462019-10-17 20:09:45 -07001212 // Add punt rule for LLDP and BDDP
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001213 TrafficSelector.Builder lldpSelector = DefaultTrafficSelector.builder();
1214 lldpSelector.matchEthType(EthType.EtherType.LLDP.ethType().toShort());
1215 FlowRule lldpRule = DefaultFlowRule.builder()
1216 .forDevice(deviceId)
1217 .withSelector(lldpSelector.build())
1218 .withTreatment(treatment)
1219 .withPriority(HIGHEST_PRIORITY)
1220 .fromApp(driverId)
1221 .makePermanent()
1222 .forTable(PUNT_TABLE).build();
1223 ops = ops.add(lldpRule);
1224
1225 TrafficSelector.Builder bbdpSelector = DefaultTrafficSelector.builder();
1226 bbdpSelector.matchEthType(EthType.EtherType.BDDP.ethType().toShort());
1227 FlowRule bbdpRule = DefaultFlowRule.builder()
1228 .forDevice(deviceId)
1229 .withSelector(bbdpSelector.build())
1230 .withTreatment(treatment)
1231 .withPriority(HIGHEST_PRIORITY)
1232 .fromApp(driverId)
1233 .makePermanent()
1234 .forTable(PUNT_TABLE).build();
1235 ops.add(bbdpRule);
1236
1237 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1238 @Override
1239 public void onSuccess(FlowRuleOperations ops) {
Charles Chan7f9d8462019-10-17 20:09:45 -07001240 log.info("Initialized table {} on {}", PUNT_TABLE, deviceId);
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001241 }
1242 @Override
1243 public void onError(FlowRuleOperations ops) {
Charles Chan7f9d8462019-10-17 20:09:45 -07001244 log.warn("Failed to initialize table {} on {}", PUNT_TABLE, deviceId);
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001245 }
1246 }));
1247 }
1248
1249 /**
Charles Chan0f43e472017-02-14 14:00:16 -08001250 * Builds a indirect group contains pop_vlan and punt actions.
1251 * <p>
1252 * Using group instead of immediate action to ensure that
1253 * the copy of packet on the data plane is not affected by the pop vlan action.
1254 */
1255 private void initPopVlanPuntGroup() {
Yi Tsengef19de12017-04-24 11:33:05 -07001256 GroupKey groupKey = popVlanPuntGroupKey();
Charles Chan0f43e472017-02-14 14:00:16 -08001257 TrafficTreatment bucketTreatment = DefaultTrafficTreatment.builder()
1258 .popVlan().punt().build();
1259 GroupBucket bucket =
1260 DefaultGroupBucket.createIndirectGroupBucket(bucketTreatment);
1261 GroupDescription groupDesc =
1262 new DefaultGroupDescription(
1263 deviceId,
1264 GroupDescription.Type.INDIRECT,
1265 new GroupBuckets(Collections.singletonList(bucket)),
1266 groupKey,
1267 POP_VLAN_PUNT_GROUP_ID,
1268 driverId);
1269 groupService.addGroup(groupDesc);
1270
1271 log.info("Initialized pop vlan punt group on {}", deviceId);
1272 }
Yi Tsengef19de12017-04-24 11:33:05 -07001273
1274 /**
1275 * Generates group key for a static indirect group that pop vlan and punt to
1276 * controller.
1277 *
1278 * @return the group key of the indirect table
1279 */
1280 private GroupKey popVlanPuntGroupKey() {
Charles Chan367c1c12018-10-19 16:23:28 -07001281 int hash = POP_VLAN_PUNT_GROUP_ID | (Objects.hash(deviceId) & FOUR_NIBBLE_MASK);
Yi Tsengef19de12017-04-24 11:33:05 -07001282 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
1283 }
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001284
1285 private class PopVlanPuntGroupChecker implements Runnable {
1286 @Override
1287 public void run() {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001288 try {
1289 groupCheckerLock.lock();
1290 // this can happen outside of the lock but I think it is safer
1291 // to include it here.
1292 Group group = groupService.getGroup(deviceId, popVlanPuntGroupKey());
Charles Chan4a288d92019-02-16 00:11:18 +00001293 if (group != null) {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001294 log.debug("PopVlanPuntGroupChecker: Installing {} missing rules at punt table.",
1295 flowRuleQueue.size());
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001296
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001297 // if we have pending flow rules install them
1298 if (flowRuleQueue.size() > 0) {
1299 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1300 // we should not care about the context here, it can only be add
1301 // since when removing the rules the group should be there already.
1302 flowRuleQueue.forEach(ops::add);
1303 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1304 @Override
1305 public void onSuccess(FlowRuleOperations ops) {
1306 log.debug("Applied {} pop vlan punt rules in device {}",
1307 ops.stages().get(0).size(), deviceId);
1308 }
1309
1310 @Override
1311 public void onError(FlowRuleOperations ops) {
1312 log.error("Failed to apply all pop vlan punt rules in dev {}", deviceId);
1313 }
1314 }));
1315 }
1316 // this signifies that the group is created and now
1317 // flow rules can be installed directly
1318 flowRuleQueue = null;
pier02a331a2020-01-23 11:59:56 +01001319 // Schedule with an initial delay the miss table flow rule installation
1320 // the delay is to make sure the queued flows are all installed before
1321 // pushing the table miss flow rule
1322 // TODO it can be further optimized by using context and completable future
1323 groupChecker.schedule(new TableMissFlowInstaller(), 5000, TimeUnit.MILLISECONDS);
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001324 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001325 } finally {
1326 groupCheckerLock.unlock();
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001327 }
1328 }
1329 }
pier02a331a2020-01-23 11:59:56 +01001330
1331 private class TableMissFlowInstaller implements Runnable {
1332 @Override
1333 public void run() {
1334 // Add table miss flow rule
1335 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1336 TrafficSelector.Builder defaultSelector = DefaultTrafficSelector.builder();
1337 TrafficTreatment treatment = DefaultTrafficTreatment.builder().punt().build();
1338 FlowRule defaultRule = DefaultFlowRule.builder()
1339 .forDevice(deviceId)
1340 .withSelector(defaultSelector.build())
1341 .withTreatment(treatment)
1342 .withPriority(LOWEST_PRIORITY)
1343 .fromApp(driverId)
1344 .makePermanent()
1345 .forTable(PUNT_TABLE).build();
1346 ops.add(defaultRule);
1347 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1348 @Override
1349 public void onSuccess(FlowRuleOperations ops) {
1350 log.info("Initialized table miss flow rule {} on {}", PUNT_TABLE, deviceId);
1351 }
1352 @Override
1353 public void onError(FlowRuleOperations ops) {
1354 log.warn("Failed to initialize table miss flow rule {} on {}", PUNT_TABLE, deviceId);
1355 }
1356 }));
1357 // shutdown the group checker gracefully
1358 groupChecker.shutdown();
1359 }
1360 }
Saurav Das558afec2015-05-31 17:12:48 -07001361}