blob: 47f8612422bdc412f03ee68977324f70f8a3bcdc [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 Chaundkar77958a52019-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) {
Charles Chanab591602019-01-22 17:25:04 -0800161 // create a new executor at each init and a new empty queue
162 groupChecker = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/driver",
163 "ovs-ofdpa-%d", log));
164 flowRuleQueue = new ConcurrentLinkedQueue<>();
165 groupCheckerLock = new ReentrantLock();
166 groupChecker.scheduleAtFixedRate(new PopVlanPuntGroupChecker(), 20, 50, TimeUnit.MILLISECONDS);
167 super.init(deviceId, context);
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700168 }
pier9469f3e2019-04-17 17:05:08 +0200169
Andrea Campanella7c977b92018-05-30 21:39:45 -0700170 protected void processFilter(FilteringObjective filteringObjective,
171 boolean install,
172 ApplicationId applicationId) {
pier9469f3e2019-04-17 17:05:08 +0200173 if (OfdpaPipelineUtility.isDoubleTagged(filteringObjective)) {
Andrea Campanella7c977b92018-05-30 21:39:45 -0700174 processDoubleTaggedFilter(filteringObjective, install, applicationId);
175 } else {
176 // If it is not a double-tagged filter, we fall back
177 // to the OFDPA 2.0 pipeline.
178 super.processFilter(filteringObjective, install, applicationId);
179 }
180 }
181
182 /**
Andrea Campanella7c977b92018-05-30 21:39:45 -0700183 * Determines if the forwarding objective will be used for double-tagged packets.
184 *
185 * @param fwd Forwarding objective
186 * @return True if the objective was created for double-tagged packets, false otherwise.
187 */
188 private boolean isDoubleTagged(ForwardingObjective fwd) {
189 if (fwd.nextId() != null) {
190 NextGroup next = getGroupForNextObjective(fwd.nextId());
191 if (next != null) {
192 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
193 // we only need the top level group's key
194 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
195 if (group != null) {
196 int groupId = group.id().id();
197 if (((groupId & ~TYPE_MASK) == L3_UNICAST_TYPE) &&
198 ((groupId & TYPE_L3UG_DOUBLE_VLAN_MASK) == TYPE_L3UG_DOUBLE_VLAN_MASK)) {
199 return true;
200 }
201 }
202 }
203 }
204 return false;
205 }
206
207 /**
208 * Configure filtering rules of outer and inner VLAN IDs, and a MAC address.
209 * Filtering happens in three tables (VLAN_TABLE, VLAN_1_TABLE, TMAC_TABLE).
210 *
211 * @param filteringObjective the filtering objective
212 * @param install true to add, false to remove
213 * @param applicationId for application programming this filter
214 */
215 private void processDoubleTaggedFilter(FilteringObjective filteringObjective,
216 boolean install,
217 ApplicationId applicationId) {
218 PortCriterion portCriterion = null;
219 EthCriterion ethCriterion = null;
220 VlanIdCriterion innervidCriterion = null;
221 VlanIdCriterion outerVidCriterion = null;
222 boolean popVlan = false;
223 TrafficTreatment meta = filteringObjective.meta();
224 if (!filteringObjective.key().equals(Criteria.dummy()) &&
225 filteringObjective.key().type() == Criterion.Type.IN_PORT) {
226 portCriterion = (PortCriterion) filteringObjective.key();
227 }
228 if (portCriterion == null) {
229 log.warn("No IN_PORT defined in filtering objective from app: {}" +
230 "Failed to program VLAN tables.", applicationId);
231 return;
232 } else {
233 log.debug("Received filtering objective for dev/port: {}/{}", deviceId,
234 portCriterion.port());
235 }
236
237 // meta should have only one instruction, popVlan.
238 if (meta != null && meta.allInstructions().size() == 1) {
239 L2ModificationInstruction l2Inst = (L2ModificationInstruction) meta.allInstructions().get(0);
240 if (l2Inst.subtype().equals(L2ModificationInstruction.L2SubType.VLAN_POP)) {
241 popVlan = true;
242 } else {
243 log.warn("Filtering objective can have only VLAN_POP instruction.");
244 return;
245 }
246 } else {
247 log.warn("Filtering objective should have one instruction.");
248 return;
249 }
250
251 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
252 for (Criterion criterion : filteringObjective.conditions()) {
253 switch (criterion.type()) {
254 case ETH_DST:
255 case ETH_DST_MASKED:
256 ethCriterion = (EthCriterion) criterion;
257 break;
258 case VLAN_VID:
Daniele Morofa382c22019-07-12 17:58:54 -0700259 outerVidCriterion = (VlanIdCriterion) criterion;
260 break;
261 case INNER_VLAN_VID:
262 innervidCriterion = (VlanIdCriterion) criterion;
Andrea Campanella7c977b92018-05-30 21:39:45 -0700263 break;
264 default:
265 log.warn("Unsupported filter {}", criterion);
266 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
267 return;
268 }
269 }
270
271 if (innervidCriterion == null || outerVidCriterion == null) {
272 log.warn("filtering objective should have two vidCriterion.");
273 return;
274 }
275
276 if (ethCriterion == null || ethCriterion.mac().equals(NONE)) {
277 // NOTE: it is possible that a filtering objective only has vidCriterion
278 log.warn("filtering objective missing dstMac, cannot program TMAC table");
279 return;
280 } else {
281 MacAddress unicastMac = readEthDstFromTreatment(filteringObjective.meta());
282 List<List<FlowRule>> allStages = processEthDstFilter(portCriterion, ethCriterion, innervidCriterion,
283 innervidCriterion.vlanId(), unicastMac,
284 applicationId);
285 for (List<FlowRule> flowRules : allStages) {
286 log.trace("Starting a new flow rule stage for TMAC table flow");
287 ops.newStage();
288
289 for (FlowRule flowRule : flowRules) {
290 log.trace("{} flow rules in TMAC table: {} for dev: {}",
291 (install) ? "adding" : "removing", flowRules, deviceId);
292 if (install) {
293 ops = ops.add(flowRule);
294 } else {
295 // NOTE: Only remove TMAC flow when there is no more enabled port within the
296 // same VLAN on this device if TMAC doesn't support matching on in_port.
297 if (matchInPortTmacTable()
298 || (filteringObjective.meta() != null
299 && filteringObjective.meta().clearedDeferred())) {
300 ops = ops.remove(flowRule);
301 } else {
302 log.debug("Abort TMAC flow removal on {}. Some other ports still share this TMAC flow");
303 }
304 }
305 }
306 }
307 }
308
309 List<FlowRule> rules;
310 rules = processDoubleVlanIdFilter(portCriterion, innervidCriterion,
311 outerVidCriterion, popVlan, applicationId);
312 for (FlowRule flowRule : rules) {
313 log.trace("{} flow rule in VLAN table: {} for dev: {}",
314 (install) ? "adding" : "removing", flowRule, deviceId);
315 ops = install ? ops.add(flowRule) : ops.remove(flowRule);
316 }
317
318 // apply filtering flow rules
319 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
320 @Override
321 public void onSuccess(FlowRuleOperations ops) {
322 log.debug("Applied {} filtering rules in device {}",
323 ops.stages().get(0).size(), deviceId);
324 pass(filteringObjective);
325 }
326
327 @Override
328 public void onError(FlowRuleOperations ops) {
329 log.info("Failed to apply all filtering rules in dev {}", deviceId);
330 fail(filteringObjective, ObjectiveError.FLOWINSTALLATIONFAILED);
331 }
332 }));
333
334 }
335 /**
336 * Internal implementation of processDoubleVlanIdFilter.
337 *
338 * @param portCriterion port on device for which this filter is programmed
339 * @param innerVidCriterion inner vlan
340 * @param outerVidCriterion outer vlan
341 * @param popVlan true if outer vlan header needs to be removed
342 * @param applicationId for application programming this filter
343 * @return flow rules for port-vlan filters
344 */
345 private List<FlowRule> processDoubleVlanIdFilter(PortCriterion portCriterion,
346 VlanIdCriterion innerVidCriterion,
347 VlanIdCriterion outerVidCriterion,
348 boolean popVlan,
349 ApplicationId applicationId) {
350 List<FlowRule> rules = new ArrayList<>();
351 TrafficSelector.Builder outerSelector = DefaultTrafficSelector.builder();
352 TrafficTreatment.Builder outerTreatment = DefaultTrafficTreatment.builder();
353 TrafficSelector.Builder innerSelector = DefaultTrafficSelector.builder();
354 TrafficTreatment.Builder innerTreatment = DefaultTrafficTreatment.builder();
355
356 VlanId outerVlanId = outerVidCriterion.vlanId();
357 VlanId innerVlanId = innerVidCriterion.vlanId();
358 PortNumber portNumber = portCriterion.port();
359 // Check arguments
360 if (PortNumber.ALL.equals(portNumber)
361 || outerVlanId.equals(VlanId.NONE)
362 || innerVlanId.equals(VlanId.NONE)) {
363 log.warn("Incomplete Filtering Objective. " +
364 "VLAN Table cannot be programmed for {}", deviceId);
365 return ImmutableList.of();
366 } else {
367 outerSelector.matchInPort(portNumber);
368 innerSelector.matchInPort(portNumber);
369 outerTreatment.transition(VLAN_1_TABLE);
370 innerTreatment.transition(TMAC_TABLE);
371 outerTreatment.writeMetadata(outerVlanId.toShort(), 0xFFF);
372
373 outerSelector.matchVlanId(outerVlanId);
374 innerSelector.matchVlanId(innerVlanId);
375 //force recompilation
376 //FIXME might be issue due tu /fff mask
377 innerSelector.matchMetadata(outerVlanId.toShort());
378
379 if (popVlan) {
380 outerTreatment.popVlan();
381 }
382 }
383
384 // NOTE: for double-tagged packets, restore original outer vlan
385 // before sending it to the controller.
Charles Chanab591602019-01-22 17:25:04 -0800386 GroupKey groupKey = popVlanPuntGroupKey();
387 Group group = groupService.getGroup(deviceId, groupKey);
388 if (group != null) {
389 // push outer vlan and send to controller
390 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
391 .matchInPort(portNumber)
392 .matchVlanId(innerVlanId);
393 Host host = handler().get(HostService.class).getConnectedHosts(ConnectPoint.
394 deviceConnectPoint(deviceId + "/" + portNumber.toLong())).stream().filter(h ->
395 h.vlan().equals(outerVlanId)).findFirst().orElse(null);
396 EthType vlanType = EthType.EtherType.VLAN.ethType();
397 if (host != null) {
398 vlanType = host.tpid();
Andrea Campanella7c977b92018-05-30 21:39:45 -0700399 }
Charles Chanab591602019-01-22 17:25:04 -0800400 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
401 .pushVlan(vlanType).setVlanId(outerVlanId).punt();
402
403 rules.add(DefaultFlowRule.builder()
404 .forDevice(deviceId)
405 .withSelector(sbuilder.build())
406 .withTreatment(tbuilder.build())
407 .withPriority(PacketPriority.CONTROL.priorityValue())
408 .fromApp(driverId)
409 .makePermanent()
410 .forTable(PUNT_TABLE).build());
411 } else {
412 log.info("popVlanPuntGroup not found in dev:{}", deviceId);
413 return Collections.emptyList();
Andrea Campanella7c977b92018-05-30 21:39:45 -0700414 }
Charles Chanab591602019-01-22 17:25:04 -0800415
Andrea Campanella7c977b92018-05-30 21:39:45 -0700416 FlowRule outerRule = DefaultFlowRule.builder()
417 .forDevice(deviceId)
418 .withSelector(outerSelector.build())
419 .withTreatment(outerTreatment.build())
420 .withPriority(DEFAULT_PRIORITY)
421 .fromApp(applicationId)
422 .makePermanent()
423 .forTable(VLAN_TABLE)
424 .build();
425 FlowRule innerRule = DefaultFlowRule.builder()
426 .forDevice(deviceId)
427 .withSelector(innerSelector.build())
428 .withTreatment(innerTreatment.build())
429 .withPriority(DEFAULT_PRIORITY)
430 .fromApp(applicationId)
431 .makePermanent()
432 .forTable(VLAN_1_TABLE)
433 .build();
434 rules.add(outerRule);
435 rules.add(innerRule);
436
437 return rules;
438 }
439
440 /**
441 * In the OF-DPA 2.0 pipeline, egress forwarding objectives go to the
442 * egress tables.
443 * @param fwd the forwarding objective of type 'egress'
444 * @return a collection of flow rules to be sent to the switch. An empty
445 * collection may be returned if there is a problem in processing
446 * the flow rule
447 */
448 @Override
449 protected Collection<FlowRule> processEgress(ForwardingObjective fwd) {
450 log.debug("Processing egress forwarding objective:{} in dev:{}",
451 fwd, deviceId);
452
453 List<FlowRule> rules = new ArrayList<>();
454
455 // Build selector
456 TrafficSelector.Builder sb = DefaultTrafficSelector.builder();
457 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) fwd.selector().getCriterion(Criterion.Type.VLAN_VID);
458 if (vlanIdCriterion == null) {
459 log.error("Egress forwarding objective:{} must include vlanId", fwd.id());
460 fail(fwd, ObjectiveError.BADPARAMS);
461 return rules;
462 }
463
464 Optional<Instruction> outInstr = fwd.treatment().allInstructions().stream()
465 .filter(instruction -> instruction instanceof Instructions.OutputInstruction).findFirst();
466 if (!outInstr.isPresent()) {
467 log.error("Egress forwarding objective:{} must include output port", fwd.id());
468 fail(fwd, ObjectiveError.BADPARAMS);
469 return rules;
470 }
471
472 PortNumber portNumber = ((Instructions.OutputInstruction) outInstr.get()).port();
473
474 sb.matchVlanId(vlanIdCriterion.vlanId());
475 OfdpaMatchActsetOutput actsetOutput = new OfdpaMatchActsetOutput(portNumber);
476 sb.extension(actsetOutput, deviceId);
477
478 // Build a flow rule for Egress VLAN Flow table
479 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
480 tb.transition(UNICAST_ROUTING_TABLE_1);
481 if (fwd.treatment() != null) {
482 for (Instruction instr : fwd.treatment().allInstructions()) {
483 if (instr instanceof L2ModificationInstruction &&
484 ((L2ModificationInstruction) instr).subtype() ==
485 L2ModificationInstruction.L2SubType.VLAN_ID) {
486 tb.immediate().add(instr);
487 }
488 if (instr instanceof L2ModificationInstruction &&
489 ((L2ModificationInstruction) instr).subtype() ==
490 L2ModificationInstruction.L2SubType.VLAN_PUSH) {
491 EthType ethType = ((L2ModificationInstruction.ModVlanHeaderInstruction) instr).ethernetType();
492 tb.immediate().pushVlan(ethType);
493 }
494 }
495 }
496
497 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
498 .fromApp(fwd.appId())
499 .withPriority(fwd.priority())
500 .forDevice(deviceId)
501 .withSelector(sb.build())
502 .withTreatment(tb.build())
503 .makePermanent()
504 .forTable(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
505 rules.add(ruleBuilder.build());
506 return rules;
507 }
508
509 /**
510 * Handles forwarding rules to the IP Unicast Routing.
511 *
512 * @param fwd the forwarding objective
513 * @return A collection of flow rules, or an empty set
514 */
515 protected Collection<FlowRule> processDoubleTaggedFwd(ForwardingObjective fwd) {
516 // inner for UNICAST_ROUTING_TABLE_1, outer for UNICAST_ROUTING_TABLE
517 TrafficSelector selector = fwd.selector();
518 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
519 TrafficTreatment.Builder innerTtb = DefaultTrafficTreatment.builder();
520 TrafficTreatment.Builder outerTtb = DefaultTrafficTreatment.builder();
521
522 EthTypeCriterion ethType =
523 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
524
525 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
526 sBuilder.matchEthType(Ethernet.TYPE_IPV4);
527 sBuilder.matchVlanId(VlanId.ANY);
528 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
529 if (!ipv4Dst.isMulticast() && ipv4Dst.prefixLength() == 32) {
530 sBuilder.matchIPDst(ipv4Dst);
531 if (fwd.nextId() != null) {
532 NextGroup next = getGroupForNextObjective(fwd.nextId());
533 if (next != null) {
534 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
535 // we only need the top level group's key to point the flow to it
536 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
537 if (group == null) {
538 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
539 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
540 fail(fwd, ObjectiveError.GROUPMISSING);
541 return Collections.emptySet();
542 }
543 outerTtb.immediate().setVlanId(extractDummyVlanIdFromGroupId(group.id().id()));
544 //ACTSET_OUTPUT in OVS will match output action in write_action() set.
545 outerTtb.deferred().setOutput(extractOutputPortFromGroupId(group.id().id()));
546 outerTtb.transition(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS);
547 innerTtb.deferred().group(group.id());
548 innerTtb.transition(ACL_TABLE);
549
550 FlowRule.Builder innerRuleBuilder = DefaultFlowRule.builder()
551 .fromApp(fwd.appId())
552 .withPriority(fwd.priority())
553 .forDevice(deviceId)
554 .withSelector(sBuilder.build())
555 .withTreatment(innerTtb.build())
556 .forTable(UNICAST_ROUTING_TABLE_1);
557 if (fwd.permanent()) {
558 innerRuleBuilder.makePermanent();
559 } else {
560 innerRuleBuilder.makeTemporary(fwd.timeout());
561 }
562 Collection<FlowRule> flowRuleCollection = new HashSet<>();
563 flowRuleCollection.add(innerRuleBuilder.build());
564
565 FlowRule.Builder outerRuleBuilder = DefaultFlowRule.builder()
566 .fromApp(fwd.appId())
567 .withPriority(fwd.priority())
568 .forDevice(deviceId)
569 .withSelector(sBuilder.build())
570 .withTreatment(outerTtb.build())
571 .forTable(UNICAST_ROUTING_TABLE);
572 if (fwd.permanent()) {
573 outerRuleBuilder.makePermanent();
574 } else {
575 outerRuleBuilder.makeTemporary(fwd.timeout());
576 }
577 flowRuleCollection.add(innerRuleBuilder.build());
578 flowRuleCollection.add(outerRuleBuilder.build());
579 return flowRuleCollection;
580 } else {
581 log.warn("Cannot find group for nextId:{} in dev:{}. Aborting fwd:{}",
582 fwd.nextId(), deviceId, fwd.id());
583 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
584 return Collections.emptySet();
585 }
586 } else {
587 log.warn("NextId is not specified in fwd:{}", fwd.id());
588 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
589 return Collections.emptySet();
590 }
591 }
592 }
593 return Collections.emptySet();
594 }
595
596 private static VlanId extractDummyVlanIdFromGroupId(int groupId) {
597 short vlanId = (short) ((groupId & 0x7FF8000) >> 15);
598 return VlanId.vlanId(vlanId);
599 }
600
601 private static PortNumber extractOutputPortFromGroupId(int groupId) {
602 return PortNumber.portNumber(groupId & 0x7FFF);
603 }
604
Saurav Das4ce45962015-11-24 23:21:05 -0800605 /*
Charles Chanab591602019-01-22 17:25:04 -0800606 * Open vSwitch emulation does not require the non OF-standard rules for
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700607 * matching untagged packets that ofdpa uses.
Saurav Das4ce45962015-11-24 23:21:05 -0800608 *
609 * (non-Javadoc)
610 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter
611 */
Saurav Das558afec2015-05-31 17:12:48 -0700612 @Override
Charles Chan66291502018-03-02 16:43:28 -0800613 protected List<List<FlowRule>> processVlanIdFilter(PortCriterion portCriterion,
pier6aef5b72019-06-10 17:10:26 +0200614 VlanIdCriterion vidCriterion,
615 VlanId assignedVlan,
616 ApplicationId applicationId,
617 boolean install) {
Charles Chan79769232016-07-05 16:34:39 -0700618 List<FlowRule> rules = new ArrayList<>();
Saurav Das2857f382015-11-03 14:39:27 -0800619 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
620 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
621 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800622 treatment.transition(TMAC_TABLE);
623
Saurav Das2857f382015-11-03 14:39:27 -0800624 if (vidCriterion.vlanId() == VlanId.NONE) {
625 // untagged packets are assigned vlans
626 treatment.pushVlan().setVlanId(assignedVlan);
Piere5bff482018-03-07 11:42:50 +0100627 } else if (!vidCriterion.vlanId().equals(assignedVlan)) {
628 // Rewrite with assigned vlans
629 treatment.setVlanId(assignedVlan);
Saurav Das2857f382015-11-03 14:39:27 -0800630 }
Saurav Das2857f382015-11-03 14:39:27 -0800631
632 // ofdpa cannot match on ALL portnumber, so we need to use separate
633 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800634 List<PortNumber> portnums = new ArrayList<>();
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700635 if (portCriterion != null) {
636 if (portCriterion.port() == PortNumber.ALL) {
637 for (Port port : deviceService.getPorts(deviceId)) {
638 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
639 portnums.add(port.number());
640 }
Saurav Das2857f382015-11-03 14:39:27 -0800641 }
Ray Milkeybe9f3bc2018-05-10 12:42:51 -0700642 } else {
643 portnums.add(portCriterion.port());
Saurav Das2857f382015-11-03 14:39:27 -0800644 }
Saurav Das2857f382015-11-03 14:39:27 -0800645 }
Saurav Das4f980082015-11-05 13:39:15 -0800646
Saurav Das2857f382015-11-03 14:39:27 -0800647 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800648 // NOTE: Emulating OFDPA behavior by popping off internal assigned
649 // VLAN before sending to controller
Charles Chanab591602019-01-22 17:25:04 -0800650 if (vidCriterion.vlanId() == VlanId.NONE) {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700651 try {
652 groupCheckerLock.lock();
653 if (flowRuleQueue == null) {
654 // this means that the group has been created
655 // and that groupChecker has destroyed the queue
656 log.debug("Installing punt table rule for untagged port {} and vlan {}.",
657 pnum, assignedVlan);
658 rules.add(buildPuntTableRule(pnum, assignedVlan));
659 } else {
660 // The VLAN punt group may be held back due to device initial audit.
661 // In that case, we queue all punt table flow until the group has been created.
662 log.debug("popVlanPuntGroup not found in dev:{}, queueing this flow rule.", deviceId);
663 flowRuleQueue.add(buildPuntTableRule(pnum, assignedVlan));
Andreas Pantelopoulos89369462018-05-30 14:30:24 -0700664 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700665 } finally {
666 groupCheckerLock.unlock();
Charles Chan0f43e472017-02-14 14:00:16 -0800667 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700668 } else if (vidCriterion.vlanId() != VlanId.NONE) {
669 // for tagged ports just forward to the controller
670 log.debug("Installing punt rule for tagged port {} and vlan {}.", pnum, vidCriterion.vlanId());
671 rules.add(buildPuntTableRuleTagged(pnum, vidCriterion.vlanId()));
Charles Chan0f43e472017-02-14 14:00:16 -0800672 }
673
Saurav Das4f980082015-11-05 13:39:15 -0800674 // create rest of flowrule
Saurav Das2857f382015-11-03 14:39:27 -0800675 selector.matchInPort(pnum);
676 FlowRule rule = DefaultFlowRule.builder()
677 .forDevice(deviceId)
678 .withSelector(selector.build())
679 .withTreatment(treatment.build())
680 .withPriority(DEFAULT_PRIORITY)
681 .fromApp(applicationId)
682 .makePermanent()
683 .forTable(VLAN_TABLE).build();
684 rules.add(rule);
685 }
Charles Chanf57a8252016-06-29 19:12:37 -0700686
Charles Chan66291502018-03-02 16:43:28 -0800687 return ImmutableList.of(rules);
Saurav Das2857f382015-11-03 14:39:27 -0800688 }
689
Pier Ventree0ae7a32016-11-23 09:57:42 -0800690 /**
Charles Chan0f43e472017-02-14 14:00:16 -0800691 * Creates punt table entry that matches IN_PORT and VLAN_VID and points to
692 * a group that pop vlan and punt.
693 *
694 * @param portNumber port number
695 * @param assignedVlan internally assigned vlan id
696 * @return punt table flow rule
697 */
698 private FlowRule buildPuntTableRule(PortNumber portNumber, VlanId assignedVlan) {
699 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
700 .matchInPort(portNumber)
701 .matchVlanId(assignedVlan);
702 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
Yi Tsengfa394de2017-02-01 11:26:40 -0800703 .group(new GroupId(POP_VLAN_PUNT_GROUP_ID));
Charles Chan0f43e472017-02-14 14:00:16 -0800704
705 return DefaultFlowRule.builder()
706 .forDevice(deviceId)
707 .withSelector(sbuilder.build())
708 .withTreatment(tbuilder.build())
709 .withPriority(PacketPriority.CONTROL.priorityValue())
710 .fromApp(driverId)
711 .makePermanent()
712 .forTable(PUNT_TABLE).build();
713 }
714
715 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -0700716 * Creates punt table entry that matches IN_PORT and VLAN_VID and forwards
717 * packet to controller tagged.
718 *
719 * @param portNumber port number
720 * @param packetVlan vlan tag of the packet
721 * @return punt table flow rule
722 */
723 private FlowRule buildPuntTableRuleTagged(PortNumber portNumber, VlanId packetVlan) {
724 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
725 .matchInPort(portNumber)
726 .matchVlanId(packetVlan);
727 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder().punt();
728
729 return DefaultFlowRule.builder()
730 .forDevice(deviceId)
731 .withSelector(sbuilder.build())
732 .withTreatment(tbuilder.build())
733 .withPriority(PacketPriority.CONTROL.priorityValue())
734 .fromApp(driverId)
735 .makePermanent()
736 .forTable(PUNT_TABLE).build();
737 }
738
Saurav Das8a0732e2015-11-20 15:27:53 -0800739 @Override
Charles Chan66291502018-03-02 16:43:28 -0800740 protected List<List<FlowRule>> processEthDstFilter(PortCriterion portCriterion,
Saurav Das4ce45962015-11-24 23:21:05 -0800741 EthCriterion ethCriterion,
742 VlanIdCriterion vidCriterion,
743 VlanId assignedVlan,
Charles Chan45b69ab2018-03-02 15:41:41 -0800744 MacAddress unicastMac,
Saurav Das4ce45962015-11-24 23:21:05 -0800745 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800746 // Consider PortNumber.ANY as wildcard. Match ETH_DST only
747 if (portCriterion != null && portCriterion.port() == PortNumber.ANY) {
748 return processEthDstOnlyFilter(ethCriterion, applicationId);
749 }
750
Charles Chan5b9df8d2016-03-28 22:21:40 -0700751 // Multicast MAC
752 if (ethCriterion.mask() != null) {
Charles Chan45b69ab2018-03-02 15:41:41 -0800753 return processMcastEthDstFilter(ethCriterion, assignedVlan, unicastMac, applicationId);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700754 }
755
Saurav Das4ce45962015-11-24 23:21:05 -0800756 //handling untagged packets via assigned VLAN
757 if (vidCriterion.vlanId() == VlanId.NONE) {
758 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
759 }
760 // ofdpa cannot match on ALL portnumber, so we need to use separate
761 // rules for each port.
Charles Chan50d900c2018-03-02 13:26:22 -0800762 List<PortNumber> portnums = new ArrayList<>();
Ray Milkey74e59132018-01-17 15:24:52 -0800763 if (portCriterion != null) {
764 if (portCriterion.port() == PortNumber.ALL) {
765 for (Port port : deviceService.getPorts(deviceId)) {
766 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
767 portnums.add(port.number());
768 }
Saurav Das4ce45962015-11-24 23:21:05 -0800769 }
Ray Milkey74e59132018-01-17 15:24:52 -0800770 } else {
771 portnums.add(portCriterion.port());
Saurav Das4ce45962015-11-24 23:21:05 -0800772 }
Saurav Das4ce45962015-11-24 23:21:05 -0800773 }
774
Charles Chan50d900c2018-03-02 13:26:22 -0800775 List<FlowRule> rules = new ArrayList<>();
Saurav Das4ce45962015-11-24 23:21:05 -0800776 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800777 // TMAC rules for unicast IP packets
Saurav Das4ce45962015-11-24 23:21:05 -0800778 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
779 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
780 selector.matchInPort(pnum);
781 selector.matchVlanId(vidCriterion.vlanId());
782 selector.matchEthType(Ethernet.TYPE_IPV4);
783 selector.matchEthDst(ethCriterion.mac());
Saurav Das4ce45962015-11-24 23:21:05 -0800784 treatment.transition(UNICAST_ROUTING_TABLE);
785 FlowRule rule = DefaultFlowRule.builder()
786 .forDevice(deviceId)
787 .withSelector(selector.build())
788 .withTreatment(treatment.build())
789 .withPriority(DEFAULT_PRIORITY)
790 .fromApp(applicationId)
791 .makePermanent()
792 .forTable(TMAC_TABLE).build();
793 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800794
795 // TMAC rules for MPLS packets
Saurav Das4ce45962015-11-24 23:21:05 -0800796 selector = DefaultTrafficSelector.builder();
797 treatment = DefaultTrafficTreatment.builder();
798 selector.matchInPort(pnum);
799 selector.matchVlanId(vidCriterion.vlanId());
800 selector.matchEthType(Ethernet.MPLS_UNICAST);
801 selector.matchEthDst(ethCriterion.mac());
Saurav Das4ce45962015-11-24 23:21:05 -0800802 treatment.transition(MPLS_TABLE_0);
803 rule = DefaultFlowRule.builder()
804 .forDevice(deviceId)
805 .withSelector(selector.build())
806 .withTreatment(treatment.build())
807 .withPriority(DEFAULT_PRIORITY)
808 .fromApp(applicationId)
809 .makePermanent()
810 .forTable(TMAC_TABLE).build();
811 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800812
813 // TMAC rules for IPv6 packets
Pier Ventree0ae7a32016-11-23 09:57:42 -0800814 selector = DefaultTrafficSelector.builder();
815 treatment = DefaultTrafficTreatment.builder();
816 selector.matchInPort(pnum);
817 selector.matchVlanId(vidCriterion.vlanId());
818 selector.matchEthType(Ethernet.TYPE_IPV6);
819 selector.matchEthDst(ethCriterion.mac());
Pier Ventree0ae7a32016-11-23 09:57:42 -0800820 treatment.transition(UNICAST_ROUTING_TABLE);
821 rule = DefaultFlowRule.builder()
822 .forDevice(deviceId)
823 .withSelector(selector.build())
824 .withTreatment(treatment.build())
825 .withPriority(DEFAULT_PRIORITY)
826 .fromApp(applicationId)
827 .makePermanent()
828 .forTable(TMAC_TABLE).build();
829 rules.add(rule);
Saurav Das4ce45962015-11-24 23:21:05 -0800830 }
Charles Chan66291502018-03-02 16:43:28 -0800831 return ImmutableList.of(rules);
Saurav Das4ce45962015-11-24 23:21:05 -0800832 }
833
Charles Chan5270ed02016-01-30 23:22:37 -0800834 @Override
Charles Chan66291502018-03-02 16:43:28 -0800835 protected List<List<FlowRule>> processEthDstOnlyFilter(EthCriterion ethCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700836 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800837 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
838 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
839 selector.matchEthType(Ethernet.TYPE_IPV4);
840 selector.matchEthDst(ethCriterion.mac());
Charles Chan5270ed02016-01-30 23:22:37 -0800841 treatment.transition(UNICAST_ROUTING_TABLE);
842 FlowRule rule = DefaultFlowRule.builder()
843 .forDevice(deviceId)
844 .withSelector(selector.build())
845 .withTreatment(treatment.build())
846 .withPriority(DEFAULT_PRIORITY)
847 .fromApp(applicationId)
848 .makePermanent()
849 .forTable(TMAC_TABLE).build();
Charles Chan66291502018-03-02 16:43:28 -0800850 return ImmutableList.of(ImmutableList.of(rule));
Charles Chan5270ed02016-01-30 23:22:37 -0800851 }
852
Saurav Das4ce45962015-11-24 23:21:05 -0800853 /*
Charles Chanab591602019-01-22 17:25:04 -0800854 * Open vSwitch emulation allows MPLS ECMP.
Saurav Das4ce45962015-11-24 23:21:05 -0800855 *
856 * (non-Javadoc)
857 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthTypeSpecific
858 */
859 @Override
860 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
Andrea Campanella7c977b92018-05-30 21:39:45 -0700861 if (isDoubleTagged(fwd)) {
862 return processDoubleTaggedFwd(fwd);
863 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800864 TrafficSelector selector = fwd.selector();
865 EthTypeCriterion ethType =
866 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
867 if ((ethType == null) ||
868 (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
Pier Ventree0ae7a32016-11-23 09:57:42 -0800869 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST) &&
870 (ethType.ethType().toShort() != Ethernet.TYPE_IPV6)) {
Saurav Das4ce45962015-11-24 23:21:05 -0800871 log.warn("processSpecific: Unsupported forwarding objective criteria"
872 + "ethType:{} in dev:{}", ethType, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800873 fail(fwd, ObjectiveError.UNSUPPORTED);
874 return Collections.emptySet();
875 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800876 boolean defaultRule = false;
Charles Chan50d900c2018-03-02 13:26:22 -0800877 int forTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800878 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
Flavio Castroe10fa242016-01-15 12:43:51 -0800879 TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder();
880
Saurav Das8a0732e2015-11-20 15:27:53 -0800881 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Flavio Castroe10fa242016-01-15 12:43:51 -0800882 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700883 if (ipv4Dst.isMulticast()) {
884 if (ipv4Dst.prefixLength() != 32) {
885 log.warn("Multicast specific forwarding objective can only be /32");
886 fail(fwd, ObjectiveError.BADPARAMS);
887 return ImmutableSet.of();
888 }
889 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
890 if (assignedVlan == null) {
891 log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
892 fail(fwd, ObjectiveError.BADPARAMS);
893 return ImmutableSet.of();
894 }
895 filteredSelector.matchVlanId(assignedVlan);
896 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
897 forTableId = MULTICAST_ROUTING_TABLE;
898 log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
899 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800900 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700901 if (ipv4Dst.prefixLength() == 0) {
902 // The entire IPV4_DST field is wildcarded intentionally
903 filteredSelector.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700904 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700905 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700906 }
907 forTableId = UNICAST_ROUTING_TABLE;
908 log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
909 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800910 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800911 } else if (ethType.ethType().toShort() == Ethernet.TYPE_IPV6) {
Julia Ferguson65428c32017-08-10 18:15:24 +0000912 IpPrefix ipv6Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV6_DST)).ip();
913 if (ipv6Dst.isMulticast()) {
914 if (ipv6Dst.prefixLength() != IpAddress.INET6_BIT_LENGTH) {
915 log.debug("Multicast specific IPv6 forwarding objective can only be /128");
916 fail(fwd, ObjectiveError.BADPARAMS);
917 return ImmutableSet.of();
918 }
919 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
920 if (assignedVlan == null) {
921 log.debug("VLAN ID required by multicast specific fwd obj is missing. Abort.");
922 fail(fwd, ObjectiveError.BADPARAMS);
923 return ImmutableSet.of();
924 }
925 filteredSelector.matchVlanId(assignedVlan);
926 filteredSelector.matchEthType(Ethernet.TYPE_IPV6).matchIPv6Dst(ipv6Dst);
927 forTableId = MULTICAST_ROUTING_TABLE;
928 log.debug("processing IPv6 multicast specific forwarding objective {} -> next:{}"
929 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
930 } else {
931 if (buildIpv6Selector(filteredSelector, fwd) < 0) {
932 return Collections.emptyList();
933 }
934 forTableId = UNICAST_ROUTING_TABLE;
Pier Ventree0ae7a32016-11-23 09:57:42 -0800935 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800936 } else {
937 filteredSelector
938 .matchEthType(Ethernet.MPLS_UNICAST)
939 .matchMplsLabel(((MplsCriterion)
940 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
941 MplsBosCriterion bos = (MplsBosCriterion) selector
942 .getCriterion(Criterion.Type.MPLS_BOS);
943 if (bos != null) {
944 filteredSelector.matchMplsBos(bos.mplsBos());
945 }
946 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800947 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
948 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800949 }
950
951 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
952 if (fwd.treatment() != null) {
953 for (Instruction i : fwd.treatment().allInstructions()) {
Charles Chanab591602019-01-22 17:25:04 -0800954 if (i instanceof L3ModificationInstruction) {
Charles Chan40132b32017-01-22 00:19:37 -0800955 L3ModificationInstruction l3instr = (L3ModificationInstruction) i;
956 if (l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_IN) ||
957 l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_OUT)) {
958 continue;
959 }
960 }
Charles Chan7d10b162015-12-07 18:54:45 -0800961 /*
962 * NOTE: OF-DPA does not support immediate instruction in
963 * L3 unicast and MPLS table.
964 */
965 tb.deferred().add(i);
Saurav Das8a0732e2015-11-20 15:27:53 -0800966 }
967 }
968
969 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800970 NextGroup next = getGroupForNextObjective(fwd.nextId());
971 if (next != null) {
972 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
973 // we only need the top level group's key to point the flow to it
974 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
975 if (group == null) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700976 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
977 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800978 fail(fwd, ObjectiveError.GROUPMISSING);
979 return Collections.emptySet();
980 }
981 tb.deferred().group(group.id());
Saurav Das8a0732e2015-11-20 15:27:53 -0800982 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800983 }
984 tb.transition(ACL_TABLE);
985 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
986 .fromApp(fwd.appId())
987 .withPriority(fwd.priority())
988 .forDevice(deviceId)
989 .withSelector(filteredSelector.build())
990 .withTreatment(tb.build())
991 .forTable(forTableId);
992
993 if (fwd.permanent()) {
994 ruleBuilder.makePermanent();
995 } else {
996 ruleBuilder.makeTemporary(fwd.timeout());
997 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800998 Collection<FlowRule> flowRuleCollection = new ArrayList<>();
999 flowRuleCollection.add(ruleBuilder.build());
1000 if (defaultRule) {
Pier Ventree0ae7a32016-11-23 09:57:42 -08001001 flowRuleCollection.add(
1002 defaultRoute(fwd, complementarySelector, forTableId, tb)
1003 );
Flavio Castroe10fa242016-01-15 12:43:51 -08001004 log.debug("Default rule 0.0.0.0/0 is being installed two rules");
1005 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001006 return flowRuleCollection;
Saurav Das8a0732e2015-11-20 15:27:53 -08001007 }
1008
Charles Chan1e492d32016-01-30 23:22:37 -08001009 @Override
1010 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
1011 List<FlowRule> rules = new ArrayList<>();
1012
1013 // Build filtered selector
1014 TrafficSelector selector = fwd.selector();
1015 EthCriterion ethCriterion = (EthCriterion) selector
1016 .getCriterion(Criterion.Type.ETH_DST);
1017 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
Andrea Campanella7c977b92018-05-30 21:39:45 -07001018 .getCriterion(VLAN_VID);
Charles Chan1e492d32016-01-30 23:22:37 -08001019
1020 if (vlanIdCriterion == null) {
1021 log.warn("Forwarding objective for bridging requires vlan. Not "
1022 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
1023 fail(fwd, ObjectiveError.BADPARAMS);
1024 return Collections.emptySet();
1025 }
1026
1027 TrafficSelector.Builder filteredSelectorBuilder =
1028 DefaultTrafficSelector.builder();
1029 // Do not match MacAddress for subnet broadcast entry
Charles Chand05f54b2017-02-13 11:56:54 -08001030 if (!ethCriterion.mac().equals(NONE) && !ethCriterion.mac().equals(BROADCAST)) {
Charles Chan1e492d32016-01-30 23:22:37 -08001031 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
1032 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
1033 fwd.id(), fwd.nextId(), deviceId);
1034 } else {
1035 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
1036 + "in dev:{} for vlan:{}",
1037 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
1038 }
1039 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
1040 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
1041
1042 if (fwd.treatment() != null) {
1043 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
1044 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
1045 }
1046
1047 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1048 if (fwd.nextId() != null) {
1049 NextGroup next = getGroupForNextObjective(fwd.nextId());
1050 if (next != null) {
1051 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1052 // we only need the top level group's key to point the flow to it
1053 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1054 if (group != null) {
1055 treatmentBuilder.deferred().group(group.id());
1056 } else {
1057 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1058 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
1059 fail(fwd, ObjectiveError.GROUPMISSING);
1060 return Collections.emptySet();
1061 }
1062 }
1063 }
1064 treatmentBuilder.immediate().transition(ACL_TABLE);
1065 TrafficTreatment filteredTreatment = treatmentBuilder.build();
1066
1067 // Build bridging table entries
1068 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
1069 flowRuleBuilder.fromApp(fwd.appId())
1070 .withPriority(fwd.priority())
1071 .forDevice(deviceId)
1072 .withSelector(filteredSelector)
1073 .withTreatment(filteredTreatment)
1074 .forTable(BRIDGING_TABLE);
1075 if (fwd.permanent()) {
1076 flowRuleBuilder.makePermanent();
1077 } else {
1078 flowRuleBuilder.makeTemporary(fwd.timeout());
1079 }
1080 rules.add(flowRuleBuilder.build());
1081 return rules;
1082 }
1083
Saurav Das52025962016-01-28 22:30:01 -08001084 @Override
Charles Chanab591602019-01-22 17:25:04 -08001085 protected TrafficTreatment.Builder versatileTreatmentBuilder(ForwardingObjective fwd) {
Saurav Das52025962016-01-28 22:30:01 -08001086 // XXX driver does not currently do type checking as per Tables 65-67 in
1087 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
1088 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
1089 if (fwd.treatment() != null) {
1090 for (Instruction ins : fwd.treatment().allInstructions()) {
1091 if (ins instanceof OutputInstruction) {
1092 OutputInstruction o = (OutputInstruction) ins;
Charles Chanab591602019-01-22 17:25:04 -08001093 if (PortNumber.CONTROLLER.equals(o.port())) {
Charles Chan0f43e472017-02-14 14:00:16 -08001094 ttBuilder.transition(PUNT_TABLE);
Saurav Das52025962016-01-28 22:30:01 -08001095 } else {
1096 log.warn("Only allowed treatments in versatile forwarding "
1097 + "objectives are punts to the controller");
1098 }
Charles Chanf76de302018-06-15 18:54:18 -07001099 } else if (ins instanceof NoActionInstruction) {
1100 // No action is allowed and nothing needs to be done
Saurav Das52025962016-01-28 22:30:01 -08001101 } else {
1102 log.warn("Cannot process instruction in versatile fwd {}", ins);
1103 }
1104 }
Charles Chan2df0e8a2017-01-09 11:45:08 -08001105 if (fwd.treatment().clearedDeferred()) {
1106 ttBuilder.wipeDeferred();
1107 }
Saurav Das52025962016-01-28 22:30:01 -08001108 }
1109 if (fwd.nextId() != null) {
Charles Chanab591602019-01-22 17:25:04 -08001110 // Override case
Saurav Das52025962016-01-28 22:30:01 -08001111 NextGroup next = getGroupForNextObjective(fwd.nextId());
Charles Chanab591602019-01-22 17:25:04 -08001112 if (next == null) {
1113 fail(fwd, ObjectiveError.BADPARAMS);
1114 return null;
1115 }
Saurav Das52025962016-01-28 22:30:01 -08001116 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1117 // we only need the top level group's key to point the flow to it
1118 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1119 if (group == null) {
1120 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
Charles Chanab591602019-01-22 17:25:04 -08001121 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das52025962016-01-28 22:30:01 -08001122 fail(fwd, ObjectiveError.GROUPMISSING);
Charles Chanab591602019-01-22 17:25:04 -08001123 return null;
Saurav Das52025962016-01-28 22:30:01 -08001124 }
1125 ttBuilder.deferred().group(group.id());
1126 }
Charles Chanab591602019-01-22 17:25:04 -08001127 return ttBuilder;
Saurav Das52025962016-01-28 22:30:01 -08001128 }
1129
1130 /*
Charles Chanab591602019-01-22 17:25:04 -08001131 * Open vSwitch emulation requires table-miss-entries in forwarding tables.
Saurav Das52025962016-01-28 22:30:01 -08001132 * Real OFDPA does not require these rules as they are put in by default.
1133 *
1134 * (non-Javadoc)
1135 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#initializePipeline()
1136 */
Saurav Das2857f382015-11-03 14:39:27 -08001137 @Override
Saurav Das558afec2015-05-31 17:12:48 -07001138 protected void initializePipeline() {
Charles Chanf6ec1532017-02-08 16:10:40 -08001139 initTableMiss(PORT_TABLE, VLAN_TABLE, null);
1140 initTableMiss(VLAN_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001141 initTableMiss(VLAN_1_TABLE, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001142 initTableMiss(TMAC_TABLE, BRIDGING_TABLE, null);
1143 initTableMiss(UNICAST_ROUTING_TABLE, ACL_TABLE, null);
1144 initTableMiss(MULTICAST_ROUTING_TABLE, ACL_TABLE, null);
Andrea Campanella7c977b92018-05-30 21:39:45 -07001145 initTableMiss(EGRESS_VLAN_FLOW_TABLE_IN_INGRESS, ACL_TABLE, null);
1146 initTableMiss(UNICAST_ROUTING_TABLE_1, ACL_TABLE, null);
Charles Chanf6ec1532017-02-08 16:10:40 -08001147 initTableMiss(MPLS_TABLE_0, MPLS_TABLE_1, null);
1148 initTableMiss(MPLS_TABLE_1, ACL_TABLE, null);
1149 initTableMiss(BRIDGING_TABLE, ACL_TABLE, null);
1150 initTableMiss(ACL_TABLE, -1, null);
Charles Chan0ab4c272019-10-17 20:09:45 -07001151 initPuntTable();
Charles Chan0f43e472017-02-14 14:00:16 -08001152
Charles Chanab591602019-01-22 17:25:04 -08001153 initPopVlanPuntGroup();
Saurav Das558afec2015-05-31 17:12:48 -07001154 }
1155
Charles Chanf6ec1532017-02-08 16:10:40 -08001156 /**
1157 * Install table-miss flow entry.
1158 *
1159 * If treatment exists, use it directly.
1160 * Else if treatment does not exist but nextTable > 0, transit to next table.
1161 * Else apply empty treatment.
1162 *
1163 * @param thisTable this table ID
1164 * @param nextTable next table ID
1165 * @param treatment traffic treatment to apply.
1166 */
1167 private void initTableMiss(int thisTable, int nextTable, TrafficTreatment treatment) {
Saurav Das558afec2015-05-31 17:12:48 -07001168 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chanf6ec1532017-02-08 16:10:40 -08001169 TrafficSelector selector = DefaultTrafficSelector.builder().build();
Saurav Das558afec2015-05-31 17:12:48 -07001170
Charles Chanf6ec1532017-02-08 16:10:40 -08001171 if (treatment == null) {
1172 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
1173 if (nextTable > 0) {
1174 tBuilder.transition(nextTable);
Saurav Das558afec2015-05-31 17:12:48 -07001175 }
Charles Chanf6ec1532017-02-08 16:10:40 -08001176 treatment = tBuilder.build();
1177 }
Saurav Das558afec2015-05-31 17:12:48 -07001178
Charles Chanb7504392017-02-10 12:51:04 -08001179 FlowRule rule = DefaultFlowRule.builder()
1180 .forDevice(deviceId)
Charles Chanf6ec1532017-02-08 16:10:40 -08001181 .withSelector(selector)
1182 .withTreatment(treatment)
Charles Chanb7504392017-02-10 12:51:04 -08001183 .withPriority(LOWEST_PRIORITY)
1184 .fromApp(driverId)
1185 .makePermanent()
Charles Chanf6ec1532017-02-08 16:10:40 -08001186 .forTable(thisTable).build();
Charles Chanb7504392017-02-10 12:51:04 -08001187 ops = ops.add(rule);
Saurav Das2857f382015-11-03 14:39:27 -08001188
1189 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1190 @Override
1191 public void onSuccess(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001192 log.info("Initialized table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001193 }
Saurav Das2857f382015-11-03 14:39:27 -08001194 @Override
1195 public void onError(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -08001196 log.warn("Failed to initialize table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -08001197 }
1198 }));
1199 }
Charles Chan0f43e472017-02-14 14:00:16 -08001200
1201 /**
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001202 * Install lldp/bbdp matching rules at table PUNT_TABLE
1203 * that forward traffic to controller.
1204 *
1205 */
Charles Chan0ab4c272019-10-17 20:09:45 -07001206 private void initPuntTable() {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001207 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1208 TrafficTreatment treatment = DefaultTrafficTreatment.builder().punt().build();
1209
Charles Chan0ab4c272019-10-17 20:09:45 -07001210 // Add punt rule for LLDP and BDDP
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001211 TrafficSelector.Builder lldpSelector = DefaultTrafficSelector.builder();
1212 lldpSelector.matchEthType(EthType.EtherType.LLDP.ethType().toShort());
1213 FlowRule lldpRule = DefaultFlowRule.builder()
1214 .forDevice(deviceId)
1215 .withSelector(lldpSelector.build())
1216 .withTreatment(treatment)
1217 .withPriority(HIGHEST_PRIORITY)
1218 .fromApp(driverId)
1219 .makePermanent()
1220 .forTable(PUNT_TABLE).build();
1221 ops = ops.add(lldpRule);
1222
1223 TrafficSelector.Builder bbdpSelector = DefaultTrafficSelector.builder();
1224 bbdpSelector.matchEthType(EthType.EtherType.BDDP.ethType().toShort());
1225 FlowRule bbdpRule = DefaultFlowRule.builder()
1226 .forDevice(deviceId)
1227 .withSelector(bbdpSelector.build())
1228 .withTreatment(treatment)
1229 .withPriority(HIGHEST_PRIORITY)
1230 .fromApp(driverId)
1231 .makePermanent()
1232 .forTable(PUNT_TABLE).build();
1233 ops.add(bbdpRule);
1234
Charles Chan0ab4c272019-10-17 20:09:45 -07001235 // Add table miss flow rule
1236 TrafficSelector.Builder defaultSelector = DefaultTrafficSelector.builder();
1237 FlowRule defaultRule = DefaultFlowRule.builder()
1238 .forDevice(deviceId)
1239 .withSelector(defaultSelector.build())
1240 .withTreatment(treatment)
1241 .withPriority(LOWEST_PRIORITY)
1242 .fromApp(driverId)
1243 .makePermanent()
1244 .forTable(PUNT_TABLE).build();
1245 ops.add(defaultRule);
1246
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001247 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1248 @Override
1249 public void onSuccess(FlowRuleOperations ops) {
Charles Chan0ab4c272019-10-17 20:09:45 -07001250 log.info("Initialized table {} on {}", PUNT_TABLE, deviceId);
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001251 }
1252 @Override
1253 public void onError(FlowRuleOperations ops) {
Charles Chan0ab4c272019-10-17 20:09:45 -07001254 log.warn("Failed to initialize table {} on {}", PUNT_TABLE, deviceId);
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001255 }
1256 }));
1257 }
1258
1259 /**
Charles Chan0f43e472017-02-14 14:00:16 -08001260 * Builds a indirect group contains pop_vlan and punt actions.
1261 * <p>
1262 * Using group instead of immediate action to ensure that
1263 * the copy of packet on the data plane is not affected by the pop vlan action.
1264 */
1265 private void initPopVlanPuntGroup() {
Yi Tsengef19de12017-04-24 11:33:05 -07001266 GroupKey groupKey = popVlanPuntGroupKey();
Charles Chan0f43e472017-02-14 14:00:16 -08001267 TrafficTreatment bucketTreatment = DefaultTrafficTreatment.builder()
1268 .popVlan().punt().build();
1269 GroupBucket bucket =
1270 DefaultGroupBucket.createIndirectGroupBucket(bucketTreatment);
1271 GroupDescription groupDesc =
1272 new DefaultGroupDescription(
1273 deviceId,
1274 GroupDescription.Type.INDIRECT,
1275 new GroupBuckets(Collections.singletonList(bucket)),
1276 groupKey,
1277 POP_VLAN_PUNT_GROUP_ID,
1278 driverId);
1279 groupService.addGroup(groupDesc);
1280
1281 log.info("Initialized pop vlan punt group on {}", deviceId);
1282 }
Yi Tsengef19de12017-04-24 11:33:05 -07001283
1284 /**
1285 * Generates group key for a static indirect group that pop vlan and punt to
1286 * controller.
1287 *
1288 * @return the group key of the indirect table
1289 */
1290 private GroupKey popVlanPuntGroupKey() {
Charles Chan367c1c12018-10-19 16:23:28 -07001291 int hash = POP_VLAN_PUNT_GROUP_ID | (Objects.hash(deviceId) & FOUR_NIBBLE_MASK);
Yi Tsengef19de12017-04-24 11:33:05 -07001292 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
1293 }
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001294
1295 private class PopVlanPuntGroupChecker implements Runnable {
1296 @Override
1297 public void run() {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001298 try {
1299 groupCheckerLock.lock();
1300 // this can happen outside of the lock but I think it is safer
1301 // to include it here.
1302 Group group = groupService.getGroup(deviceId, popVlanPuntGroupKey());
Charles Chan4a288d92019-02-16 00:11:18 +00001303 if (group != null) {
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001304 log.debug("PopVlanPuntGroupChecker: Installing {} missing rules at punt table.",
1305 flowRuleQueue.size());
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001306
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001307 // if we have pending flow rules install them
1308 if (flowRuleQueue.size() > 0) {
1309 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
1310 // we should not care about the context here, it can only be add
1311 // since when removing the rules the group should be there already.
1312 flowRuleQueue.forEach(ops::add);
1313 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
1314 @Override
1315 public void onSuccess(FlowRuleOperations ops) {
1316 log.debug("Applied {} pop vlan punt rules in device {}",
1317 ops.stages().get(0).size(), deviceId);
1318 }
1319
1320 @Override
1321 public void onError(FlowRuleOperations ops) {
1322 log.error("Failed to apply all pop vlan punt rules in dev {}", deviceId);
1323 }
1324 }));
1325 }
1326 // this signifies that the group is created and now
1327 // flow rules can be installed directly
1328 flowRuleQueue = null;
1329 // shutdown the group checker gracefully
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001330 groupChecker.shutdown();
1331 }
Andreas Pantelopoulos470865e2018-06-04 15:10:35 -07001332 } finally {
1333 groupCheckerLock.unlock();
Andreas Pantelopoulos89369462018-05-30 14:30:24 -07001334 }
1335 }
1336 }
Saurav Das558afec2015-05-31 17:12:48 -07001337}