blob: 1abe86ca1f53c41b8e5bb78ae8b479442f53adad [file] [log] [blame]
Saurav Das822c4e22015-10-23 10:51:11 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Saurav Das822c4e22015-10-23 10:51:11 -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 Das822c4e22015-10-23 10:51:11 -070017
Charles Chan5270ed02016-01-30 23:22:37 -080018import com.google.common.collect.ImmutableList;
Yi Tseng47f82dc2017-03-05 22:48:39 -080019import com.google.common.collect.Sets;
Saurav Das822c4e22015-10-23 10:51:11 -070020import org.onlab.osgi.ServiceDirectory;
Saurav Das822c4e22015-10-23 10:51:11 -070021import org.onlab.packet.Ethernet;
Charles Chan14967c22015-12-07 11:11:50 -080022import org.onlab.packet.IpPrefix;
Saurav Das822c4e22015-10-23 10:51:11 -070023import org.onlab.packet.VlanId;
24import org.onlab.util.KryoNamespace;
25import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
Charles Chancad338a2016-09-16 18:03:11 -070027import org.onosproject.driver.extensions.Ofdpa3MplsType;
28import org.onosproject.driver.extensions.Ofdpa3SetMplsType;
Charles Chan14967c22015-12-07 11:11:50 -080029import org.onosproject.driver.extensions.OfdpaMatchVlanVid;
30import org.onosproject.driver.extensions.OfdpaSetVlanVid;
Saurav Das822c4e22015-10-23 10:51:11 -070031import org.onosproject.net.DeviceId;
32import org.onosproject.net.Port;
33import org.onosproject.net.PortNumber;
34import org.onosproject.net.behaviour.NextGroup;
35import org.onosproject.net.behaviour.Pipeliner;
36import org.onosproject.net.behaviour.PipelinerContext;
37import org.onosproject.net.device.DeviceService;
38import org.onosproject.net.driver.AbstractHandlerBehaviour;
39import org.onosproject.net.flow.DefaultFlowRule;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.FlowRule;
43import org.onosproject.net.flow.FlowRuleOperations;
44import org.onosproject.net.flow.FlowRuleOperationsContext;
45import org.onosproject.net.flow.FlowRuleService;
46import org.onosproject.net.flow.TrafficSelector;
47import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.flow.criteria.Criteria;
49import org.onosproject.net.flow.criteria.Criterion;
50import org.onosproject.net.flow.criteria.EthCriterion;
51import org.onosproject.net.flow.criteria.EthTypeCriterion;
Charles Chan14967c22015-12-07 11:11:50 -080052import org.onosproject.net.flow.criteria.ExtensionCriterion;
Saurav Das822c4e22015-10-23 10:51:11 -070053import org.onosproject.net.flow.criteria.IPCriterion;
Pier Ventree0ae7a32016-11-23 09:57:42 -080054import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion;
55import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080056import org.onosproject.net.flow.criteria.MplsBosCriterion;
57import org.onosproject.net.flow.criteria.MplsCriterion;
Saurav Das822c4e22015-10-23 10:51:11 -070058import org.onosproject.net.flow.criteria.PortCriterion;
59import org.onosproject.net.flow.criteria.VlanIdCriterion;
60import org.onosproject.net.flow.instructions.Instruction;
61import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
62import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Saurav Das8a0732e2015-11-20 15:27:53 -080063import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
Saurav Das822c4e22015-10-23 10:51:11 -070064import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Charles Chan14967c22015-12-07 11:11:50 -080065import org.onosproject.net.flow.instructions.L3ModificationInstruction;
66import org.onosproject.net.flow.instructions.L3ModificationInstruction.L3SubType;
Saurav Das822c4e22015-10-23 10:51:11 -070067import org.onosproject.net.flowobjective.FilteringObjective;
68import org.onosproject.net.flowobjective.FlowObjectiveStore;
69import org.onosproject.net.flowobjective.ForwardingObjective;
70import org.onosproject.net.flowobjective.NextObjective;
71import org.onosproject.net.flowobjective.Objective;
72import org.onosproject.net.flowobjective.ObjectiveError;
Saurav Das822c4e22015-10-23 10:51:11 -070073import org.onosproject.net.group.DefaultGroupKey;
74import org.onosproject.net.group.Group;
Saurav Das822c4e22015-10-23 10:51:11 -070075import org.onosproject.net.group.GroupKey;
Saurav Das822c4e22015-10-23 10:51:11 -070076import org.onosproject.net.group.GroupService;
Saurav Das822c4e22015-10-23 10:51:11 -070077import org.onosproject.store.serializers.KryoNamespaces;
78import org.slf4j.Logger;
79
Pier Ventree0ae7a32016-11-23 09:57:42 -080080import java.util.ArrayDeque;
81import java.util.ArrayList;
82import java.util.Collection;
83import java.util.Collections;
84import java.util.Deque;
85import java.util.List;
86import java.util.Objects;
Pier Ventree0ae7a32016-11-23 09:57:42 -080087import java.util.concurrent.ScheduledExecutorService;
88import java.util.concurrent.TimeUnit;
89
90import static java.util.concurrent.Executors.newScheduledThreadPool;
Yi Tseng3a77b4f2017-02-06 15:02:17 -080091import static org.onlab.packet.MacAddress.BROADCAST;
92import static org.onlab.packet.MacAddress.NONE;
Pier Ventree0ae7a32016-11-23 09:57:42 -080093import static org.onlab.util.Tools.groupedThreads;
Yi Tsengef19de12017-04-24 11:33:05 -070094import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*;
Pier Ventree0ae7a32016-11-23 09:57:42 -080095import static org.slf4j.LoggerFactory.getLogger;
Pier Ventre140a8942016-11-02 07:26:38 -070096import static org.onosproject.net.flow.criteria.Criterion.Type.MPLS_BOS;
97import static org.onosproject.net.flowobjective.NextObjective.Type.HASHED;
Pier Ventree0ae7a32016-11-23 09:57:42 -080098
Saurav Das822c4e22015-10-23 10:51:11 -070099/**
100 * Driver for Broadcom's OF-DPA v2.0 TTP.
Saurav Das822c4e22015-10-23 10:51:11 -0700101 */
Charles Chan361154b2016-03-24 10:23:39 -0700102public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeliner {
Pier Ventre42287df2016-11-09 14:17:26 -0800103
Saurav Das822c4e22015-10-23 10:51:11 -0700104 protected static final int PORT_TABLE = 0;
105 protected static final int VLAN_TABLE = 10;
Pier Ventre42287df2016-11-09 14:17:26 -0800106 protected static final int VLAN_1_TABLE = 11;
107 protected static final int MPLS_L2_PORT_FLOW_TABLE = 13;
108 protected static final int MPLS_L2_PORT_PCP_TRUST_FLOW_TABLE = 16;
Saurav Das822c4e22015-10-23 10:51:11 -0700109 protected static final int TMAC_TABLE = 20;
110 protected static final int UNICAST_ROUTING_TABLE = 30;
111 protected static final int MULTICAST_ROUTING_TABLE = 40;
112 protected static final int MPLS_TABLE_0 = 23;
113 protected static final int MPLS_TABLE_1 = 24;
Pier Ventre140a8942016-11-02 07:26:38 -0700114 protected static final int MPLS_L3_TYPE_TABLE = 27;
115 protected static final int MPLS_TYPE_TABLE = 29;
Saurav Das822c4e22015-10-23 10:51:11 -0700116 protected static final int BRIDGING_TABLE = 50;
117 protected static final int ACL_TABLE = 60;
118 protected static final int MAC_LEARNING_TABLE = 254;
119 protected static final long OFPP_MAX = 0xffffff00L;
120
Saurav Das52025962016-01-28 22:30:01 -0800121 protected static final int HIGHEST_PRIORITY = 0xffff;
Saurav Das2857f382015-11-03 14:39:27 -0800122 protected static final int DEFAULT_PRIORITY = 0x8000;
Saurav Das822c4e22015-10-23 10:51:11 -0700123 protected static final int LOWEST_PRIORITY = 0x0;
124
Pier Ventre42287df2016-11-09 14:17:26 -0800125 protected static final int MPLS_L2_PORT_PRIORITY = 2;
126
127 protected static final int MPLS_TUNNEL_ID_BASE = 0x10000;
128 protected static final int MPLS_TUNNEL_ID_MAX = 0x1FFFF;
129
Pier Ventre70d53ba2016-11-17 22:26:29 -0800130 protected static final int MPLS_UNI_PORT_MAX = 0x0000FFFF;
131
132 protected static final int MPLS_NNI_PORT_BASE = 0x00020000;
133 protected static final int MPLS_NNI_PORT_MAX = 0x0002FFFF;
134
Saurav Das822c4e22015-10-23 10:51:11 -0700135 private final Logger log = getLogger(getClass());
Charles Chan425854b2016-04-11 15:32:12 -0700136 protected ServiceDirectory serviceDirectory;
Saurav Das822c4e22015-10-23 10:51:11 -0700137 protected FlowRuleService flowRuleService;
Charles Chan425854b2016-04-11 15:32:12 -0700138 protected CoreService coreService;
Saurav Das8a0732e2015-11-20 15:27:53 -0800139 protected GroupService groupService;
140 protected FlowObjectiveStore flowObjectiveStore;
Saurav Das822c4e22015-10-23 10:51:11 -0700141 protected DeviceId deviceId;
142 protected ApplicationId driverId;
Saurav Das822c4e22015-10-23 10:51:11 -0700143 protected DeviceService deviceService;
Charles Chan188ebf52015-12-23 00:15:11 -0800144 protected static KryoNamespace appKryo = new KryoNamespace.Builder()
Yi Tsengef19de12017-04-24 11:33:05 -0700145 .register(KryoNamespaces.API)
146 .register(GroupKey.class)
147 .register(DefaultGroupKey.class)
148 .register(OfdpaNextGroup.class)
149 .register(ArrayDeque.class)
150 .build("Ofdpa2Pipeline");
Saurav Das822c4e22015-10-23 10:51:11 -0700151
Charles Chan425854b2016-04-11 15:32:12 -0700152 protected Ofdpa2GroupHandler groupHandler;
Saurav Das822c4e22015-10-23 10:51:11 -0700153
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700154 // flows installations to be retried
155 protected ScheduledExecutorService executorService
156 = newScheduledThreadPool(5, groupedThreads("OfdpaPipeliner", "retry-%d", log));
157 protected static final int MAX_RETRY_ATTEMPTS = 10;
158 protected static final int RETRY_MS = 1000;
Saurav Das4f980082015-11-05 13:39:15 -0800159
Saurav Das822c4e22015-10-23 10:51:11 -0700160 @Override
161 public void init(DeviceId deviceId, PipelinerContext context) {
Saurav Das822c4e22015-10-23 10:51:11 -0700162 this.deviceId = deviceId;
163
Charles Chan425854b2016-04-11 15:32:12 -0700164 serviceDirectory = context.directory();
Saurav Das822c4e22015-10-23 10:51:11 -0700165 coreService = serviceDirectory.get(CoreService.class);
166 flowRuleService = serviceDirectory.get(FlowRuleService.class);
167 groupService = serviceDirectory.get(GroupService.class);
168 flowObjectiveStore = context.store();
Saurav Das822c4e22015-10-23 10:51:11 -0700169 deviceService = serviceDirectory.get(DeviceService.class);
Saurav Das822c4e22015-10-23 10:51:11 -0700170
Charles Chan40132b32017-01-22 00:19:37 -0800171 initDriverId();
172 initGroupHander(context);
Saurav Das822c4e22015-10-23 10:51:11 -0700173
Saurav Das822c4e22015-10-23 10:51:11 -0700174 initializePipeline();
Saurav Das822c4e22015-10-23 10:51:11 -0700175 }
176
Charles Chan40132b32017-01-22 00:19:37 -0800177 protected void initDriverId() {
178 driverId = coreService.registerApplication(
179 "org.onosproject.driver.Ofdpa2Pipeline");
180 }
181
182 protected void initGroupHander(PipelinerContext context) {
183 groupHandler = new Ofdpa2GroupHandler();
184 groupHandler.init(deviceId, context);
185 }
186
Saurav Das822c4e22015-10-23 10:51:11 -0700187 protected void initializePipeline() {
Charles Chan188ebf52015-12-23 00:15:11 -0800188 // OF-DPA does not require initializing the pipeline as it puts default
189 // rules automatically in the hardware. However emulation of OFDPA in
190 // software switches does require table-miss-entries.
Saurav Das822c4e22015-10-23 10:51:11 -0700191 }
192
Charles Chand1172632017-03-15 17:33:09 -0700193 /**
194 * Determines whether this pipeline requires OFDPA match and set VLAN extensions.
195 *
196 * @return true to use the extensions
197 */
198 protected boolean requireVlanExtensions() {
199 return true;
200 }
201
Saurav Das86d13e82017-04-28 17:03:48 -0700202 /**
203 * Determines whether in-port should be matched on in TMAC table rules.
204 *
205 * @return true if match on in-port should be programmed
206 */
207 protected boolean matchInPortTmacTable() {
208 return true;
209 }
210
Saurav Das822c4e22015-10-23 10:51:11 -0700211 //////////////////////////////////////
212 // Flow Objectives
213 //////////////////////////////////////
214
215 @Override
216 public void filter(FilteringObjective filteringObjective) {
217 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
218 processFilter(filteringObjective,
219 filteringObjective.op() == Objective.Operation.ADD,
220 filteringObjective.appId());
221 } else {
222 // Note that packets that don't match the PERMIT filter are
223 // automatically denied. The DENY filter is used to deny packets
224 // that are otherwise permitted by the PERMIT filter.
225 // Use ACL table flow rules here for DENY filtering objectives
226 log.debug("filter objective other than PERMIT currently not supported");
227 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
228 }
229 }
230
231 @Override
232 public void forward(ForwardingObjective fwd) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700233 Collection<FlowRule> rules = processForward(fwd);
Saurav Das25190812016-05-27 13:54:07 -0700234 if (rules == null || rules.isEmpty()) {
235 // Assumes fail message has already been generated to the objective
236 // context. Returning here prevents spurious pass message to be
237 // generated by FlowRule service for empty flowOps.
238 return;
239 }
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700240 sendForward(fwd, rules);
241 }
242
243 protected void sendForward(ForwardingObjective fwd, Collection<FlowRule> rules) {
244 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
Saurav Das822c4e22015-10-23 10:51:11 -0700245 switch (fwd.op()) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700246 case ADD:
247 rules.stream()
248 .filter(Objects::nonNull)
249 .forEach(flowOpsBuilder::add);
Saurav Dasd2fded02016-12-02 15:43:47 -0800250 log.debug("Applying a add fwd-obj {} to sw:{}", fwd.id(), deviceId);
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700251 break;
252 case REMOVE:
253 rules.stream()
254 .filter(Objects::nonNull)
255 .forEach(flowOpsBuilder::remove);
Pier Ventre42287df2016-11-09 14:17:26 -0800256 log.debug("Deleting a flow rule to sw:{}", deviceId);
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700257 break;
258 default:
259 fail(fwd, ObjectiveError.UNKNOWN);
260 log.warn("Unknown forwarding type {}", fwd.op());
Saurav Das822c4e22015-10-23 10:51:11 -0700261 }
262
Saurav Das822c4e22015-10-23 10:51:11 -0700263 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
264 @Override
265 public void onSuccess(FlowRuleOperations ops) {
266 pass(fwd);
267 }
268
269 @Override
270 public void onError(FlowRuleOperations ops) {
271 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
272 }
273 }));
Saurav Das822c4e22015-10-23 10:51:11 -0700274 }
275
276 @Override
277 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800278 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
279 switch (nextObjective.op()) {
280 case ADD:
Saurav Das4f980082015-11-05 13:39:15 -0800281 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800282 log.warn("Cannot add next {} that already exists in device {}",
283 nextObjective.id(), deviceId);
284 return;
285 }
286 log.debug("Processing NextObjective id{} in dev{} - add group",
287 nextObjective.id(), deviceId);
Charles Chan425854b2016-04-11 15:32:12 -0700288 groupHandler.addGroup(nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800289 break;
290 case ADD_TO_EXISTING:
291 if (nextGroup != null) {
292 log.debug("Processing NextObjective id{} in dev{} - add bucket",
293 nextObjective.id(), deviceId);
Charles Chan425854b2016-04-11 15:32:12 -0700294 groupHandler.addBucketToGroup(nextObjective, nextGroup);
Saurav Das4f980082015-11-05 13:39:15 -0800295 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800296 // it is possible that group-chain has not been fully created yet
Saurav Das423fe2b2015-12-04 10:52:59 -0800297 log.debug("Waiting to add bucket to group for next-id:{} in dev:{}",
298 nextObjective.id(), deviceId);
Yi Tseng47f82dc2017-03-05 22:48:39 -0800299
300 // by design multiple pending bucket is allowed for the group
301 groupHandler.pendingBuckets.compute(nextObjective.id(), (nextId, pendBkts) -> {
302 if (pendBkts == null) {
303 pendBkts = Sets.newHashSet();
304 }
305 pendBkts.add(nextObjective);
306 return pendBkts;
307 });
Saurav Das4f980082015-11-05 13:39:15 -0800308 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800309 break;
310 case REMOVE:
311 if (nextGroup == null) {
312 log.warn("Cannot remove next {} that does not exist in device {}",
313 nextObjective.id(), deviceId);
314 return;
315 }
316 log.debug("Processing NextObjective id{} in dev{} - remove group",
317 nextObjective.id(), deviceId);
Charles Chan425854b2016-04-11 15:32:12 -0700318 groupHandler.removeGroup(nextObjective, nextGroup);
Saurav Das8a0732e2015-11-20 15:27:53 -0800319 break;
320 case REMOVE_FROM_EXISTING:
321 if (nextGroup == null) {
322 log.warn("Cannot remove from next {} that does not exist in device {}",
323 nextObjective.id(), deviceId);
324 return;
325 }
326 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
327 nextObjective.id(), deviceId);
Charles Chan425854b2016-04-11 15:32:12 -0700328 groupHandler.removeBucketFromGroup(nextObjective, nextGroup);
Saurav Das8a0732e2015-11-20 15:27:53 -0800329 break;
330 default:
Saurav Das4f980082015-11-05 13:39:15 -0800331 log.warn("Unsupported operation {}", nextObjective.op());
Saurav Das822c4e22015-10-23 10:51:11 -0700332 }
333 }
334
335 //////////////////////////////////////
336 // Flow handling
337 //////////////////////////////////////
338
339 /**
Yi Tseng0fdc07e2017-04-24 19:17:08 -0700340 * As per OFDPA 2.0 TTP, filtering of VLAN ids and MAC addresses (for routing)
341 * configured on switch ports happen in different tables.
Saurav Das822c4e22015-10-23 10:51:11 -0700342 *
343 * @param filt the filtering objective
344 * @param install indicates whether to add or remove the objective
345 * @param applicationId the application that sent this objective
346 */
Saurav Das52025962016-01-28 22:30:01 -0800347 protected void processFilter(FilteringObjective filt,
348 boolean install, ApplicationId applicationId) {
Saurav Das822c4e22015-10-23 10:51:11 -0700349 // This driver only processes filtering criteria defined with switch
350 // ports as the key
Yi Tseng0fdc07e2017-04-24 19:17:08 -0700351 PortCriterion portCriterion;
Saurav Das822c4e22015-10-23 10:51:11 -0700352 EthCriterion ethCriterion = null;
353 VlanIdCriterion vidCriterion = null;
Saurav Das822c4e22015-10-23 10:51:11 -0700354 if (!filt.key().equals(Criteria.dummy()) &&
355 filt.key().type() == Criterion.Type.IN_PORT) {
356 portCriterion = (PortCriterion) filt.key();
357 } else {
358 log.warn("No key defined in filtering objective from app: {}. Not"
359 + "processing filtering objective", applicationId);
Saurav Das59232cf2016-04-27 18:35:50 -0700360 fail(filt, ObjectiveError.BADPARAMS);
Saurav Das822c4e22015-10-23 10:51:11 -0700361 return;
362 }
Saurav Das59232cf2016-04-27 18:35:50 -0700363 log.debug("Received filtering objective for dev/port: {}/{}", deviceId,
364 portCriterion.port());
Saurav Das822c4e22015-10-23 10:51:11 -0700365 // convert filtering conditions for switch-intfs into flowrules
366 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
367 for (Criterion criterion : filt.conditions()) {
Yi Tseng0fdc07e2017-04-24 19:17:08 -0700368 switch (criterion.type()) {
369 case ETH_DST:
370 case ETH_DST_MASKED:
371 ethCriterion = (EthCriterion) criterion;
372 break;
373 case VLAN_VID:
374 vidCriterion = (VlanIdCriterion) criterion;
375 break;
376 default:
377 log.warn("Unsupported filter {}", criterion);
378 fail(filt, ObjectiveError.UNSUPPORTED);
379 return;
Saurav Das822c4e22015-10-23 10:51:11 -0700380 }
381 }
382
Saurav Das0e99e2b2015-10-28 12:39:42 -0700383 VlanId assignedVlan = null;
Charles Chane849c192016-01-11 18:28:54 -0800384 if (vidCriterion != null) {
Charles Chand55e84d2016-03-30 17:54:24 -0700385 // Use the VLAN in metadata whenever a metadata is provided
386 if (filt.meta() != null) {
387 assignedVlan = readVlanFromTreatment(filt.meta());
388 // Use the VLAN in criterion if metadata is not present and the traffic is tagged
389 } else if (!vidCriterion.vlanId().equals(VlanId.NONE)) {
Charles Chane849c192016-01-11 18:28:54 -0800390 assignedVlan = vidCriterion.vlanId();
Charles Chand55e84d2016-03-30 17:54:24 -0700391 }
Charles Chane849c192016-01-11 18:28:54 -0800392
Charles Chand55e84d2016-03-30 17:54:24 -0700393 if (assignedVlan == null) {
394 log.error("Driver fails to extract VLAN information. "
395 + "Not proccessing VLAN filters on device {}.", deviceId);
396 log.debug("VLAN ID in criterion={}, metadata={}",
397 readVlanFromTreatment(filt.meta()), vidCriterion.vlanId());
398 fail(filt, ObjectiveError.BADPARAMS);
399 return;
Saurav Das0e99e2b2015-10-28 12:39:42 -0700400 }
401 }
402
Yi Tseng3a77b4f2017-02-06 15:02:17 -0800403 if (ethCriterion == null || ethCriterion.mac().equals(NONE)) {
Charles Chan985b12e2016-05-11 19:47:22 -0700404 // NOTE: it is possible that a filtering objective only has vidCriterion
Saurav Das961beb22017-03-29 19:09:17 -0700405 log.warn("filtering objective missing dstMac, cannot program TMAC table");
Saurav Das822c4e22015-10-23 10:51:11 -0700406 } else {
407 for (FlowRule tmacRule : processEthDstFilter(portCriterion, ethCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700408 vidCriterion, assignedVlan,
409 applicationId)) {
Saurav Das961beb22017-03-29 19:09:17 -0700410 log.trace("{} MAC filtering rules in TMAC table: {} for dev: {}",
Saurav Das018605f2017-02-18 14:05:44 -0800411 (install) ? "adding" : "removing", tmacRule, deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700412 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
413 }
414 }
415
Charles Chan985b12e2016-05-11 19:47:22 -0700416 if (vidCriterion == null) {
417 // NOTE: it is possible that a filtering objective only has ethCriterion
Charles Chan053b1cb2017-03-22 16:56:35 -0700418 log.debug("filtering objective missing VLAN, cannot program VLAN Table");
Saurav Das822c4e22015-10-23 10:51:11 -0700419 } else {
Charles Chan14967c22015-12-07 11:11:50 -0800420 /*
421 * NOTE: Separate vlan filtering rules and assignment rules
422 * into different stage in order to guarantee that filtering rules
Saurav Das52025962016-01-28 22:30:01 -0800423 * always go first, as required by ofdpa.
Charles Chan14967c22015-12-07 11:11:50 -0800424 */
425 List<FlowRule> allRules = processVlanIdFilter(
426 portCriterion, vidCriterion, assignedVlan, applicationId);
427 List<FlowRule> filteringRules = new ArrayList<>();
428 List<FlowRule> assignmentRules = new ArrayList<>();
429
430 allRules.forEach(flowRule -> {
Charles Chand1172632017-03-15 17:33:09 -0700431 VlanId vlanId;
432 if (requireVlanExtensions()) {
433 ExtensionCriterion extCriterion =
434 (ExtensionCriterion) flowRule.selector().getCriterion(Criterion.Type.EXTENSION);
435 vlanId = ((OfdpaMatchVlanVid) extCriterion.extensionSelector()).vlanId();
436 } else {
437 VlanIdCriterion vlanIdCriterion =
438 (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
439 vlanId = vlanIdCriterion.vlanId();
440 }
Charles Chanbe8aea42016-02-24 12:04:47 -0800441 if (!vlanId.equals(VlanId.NONE)) {
Charles Chan14967c22015-12-07 11:11:50 -0800442 filteringRules.add(flowRule);
443 } else {
444 assignmentRules.add(flowRule);
445 }
446 });
447
448 for (FlowRule filteringRule : filteringRules) {
Saurav Das961beb22017-03-29 19:09:17 -0700449 log.trace("{} VLAN filtering rule in VLAN table: {} for dev: {}",
Saurav Das018605f2017-02-18 14:05:44 -0800450 (install) ? "adding" : "removing", filteringRule, deviceId);
Charles Chan14967c22015-12-07 11:11:50 -0800451 ops = install ? ops.add(filteringRule) : ops.remove(filteringRule);
452 }
453
454 ops.newStage();
455
456 for (FlowRule assignmentRule : assignmentRules) {
Saurav Das961beb22017-03-29 19:09:17 -0700457 log.trace("{} VLAN assignment rule in VLAN table: {} for dev: {}",
Saurav Das018605f2017-02-18 14:05:44 -0800458 (install) ? "adding" : "removing", assignmentRule, deviceId);
Charles Chan14967c22015-12-07 11:11:50 -0800459 ops = install ? ops.add(assignmentRule) : ops.remove(assignmentRule);
Saurav Das822c4e22015-10-23 10:51:11 -0700460 }
461 }
462
Saurav Das822c4e22015-10-23 10:51:11 -0700463 // apply filtering flow rules
464 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
465 @Override
466 public void onSuccess(FlowRuleOperations ops) {
Saurav Das018605f2017-02-18 14:05:44 -0800467 log.debug("Applied {} filtering rules in device {}",
Saurav Das822c4e22015-10-23 10:51:11 -0700468 ops.stages().get(0).size(), deviceId);
469 pass(filt);
470 }
471
472 @Override
473 public void onError(FlowRuleOperations ops) {
474 log.info("Failed to apply all filtering rules in dev {}", deviceId);
475 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
476 }
477 }));
478
479 }
480
481 /**
Charles Chand1172632017-03-15 17:33:09 -0700482 * Internal implementation of processVlanIdFilter.
483 * <p>
484 * The is_present bit in set_vlan_vid action is required to be 0 in OFDPA i12.
485 * Since it is non-OF spec, we need an extension treatment for that.
486 * The useVlanExtension must be set to false for OFDPA i12.
487 * </p>
Saurav Das0e99e2b2015-10-28 12:39:42 -0700488 *
Charles Chanf9e98652016-09-07 16:54:23 -0700489 * @param portCriterion port on device for which this filter is programmed
490 * @param vidCriterion vlan assigned to port, or NONE for untagged
491 * @param assignedVlan assigned vlan-id for untagged packets
492 * @param applicationId for application programming this filter
Saurav Das822c4e22015-10-23 10:51:11 -0700493 * @return list of FlowRule for port-vlan filters
494 */
495 protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700496 VlanIdCriterion vidCriterion,
497 VlanId assignedVlan,
498 ApplicationId applicationId) {
Charles Chan14967c22015-12-07 11:11:50 -0800499 List<FlowRule> rules = new ArrayList<>();
Saurav Das822c4e22015-10-23 10:51:11 -0700500 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
501 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
Charles Chan14967c22015-12-07 11:11:50 -0800502 TrafficSelector.Builder preSelector = null;
503 TrafficTreatment.Builder preTreatment = null;
504
Saurav Das4f980082015-11-05 13:39:15 -0800505 treatment.transition(TMAC_TABLE);
506
Saurav Das822c4e22015-10-23 10:51:11 -0700507 if (vidCriterion.vlanId() == VlanId.NONE) {
508 // untagged packets are assigned vlans
Charles Chand1172632017-03-15 17:33:09 -0700509 preSelector = DefaultTrafficSelector.builder();
510 if (requireVlanExtensions()) {
511 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(VlanId.NONE);
512 selector.extension(ofdpaMatchVlanVid, deviceId);
Charles Chanf9e98652016-09-07 16:54:23 -0700513 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan);
514 treatment.extension(ofdpaSetVlanVid, deviceId);
Charles Chand1172632017-03-15 17:33:09 -0700515
516 OfdpaMatchVlanVid preOfdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan);
517 preSelector.extension(preOfdpaMatchVlanVid, deviceId);
Charles Chanf9e98652016-09-07 16:54:23 -0700518 } else {
Charles Chand1172632017-03-15 17:33:09 -0700519 selector.matchVlanId(VlanId.NONE);
Charles Chanf9e98652016-09-07 16:54:23 -0700520 treatment.setVlanId(assignedVlan);
Charles Chand1172632017-03-15 17:33:09 -0700521
522 preSelector.matchVlanId(assignedVlan);
523 }
524 preTreatment = DefaultTrafficTreatment.builder().transition(TMAC_TABLE);
525 } else {
526 if (requireVlanExtensions()) {
527 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vidCriterion.vlanId());
528 selector.extension(ofdpaMatchVlanVid, deviceId);
529 } else {
530 selector.matchVlanId(vidCriterion.vlanId());
Charles Chanf9e98652016-09-07 16:54:23 -0700531 }
Charles Chan14967c22015-12-07 11:11:50 -0800532
Charles Chand55e84d2016-03-30 17:54:24 -0700533 if (!assignedVlan.equals(vidCriterion.vlanId())) {
Charles Chand1172632017-03-15 17:33:09 -0700534 if (requireVlanExtensions()) {
Charles Chanc03782d2017-01-31 13:57:55 -0800535 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan);
536 treatment.extension(ofdpaSetVlanVid, deviceId);
537 } else {
538 treatment.setVlanId(assignedVlan);
539 }
Charles Chand55e84d2016-03-30 17:54:24 -0700540 }
Saurav Das822c4e22015-10-23 10:51:11 -0700541 }
Saurav Das822c4e22015-10-23 10:51:11 -0700542
543 // ofdpa cannot match on ALL portnumber, so we need to use separate
544 // rules for each port.
Charles Chan14967c22015-12-07 11:11:50 -0800545 List<PortNumber> portnums = new ArrayList<>();
Saurav Das822c4e22015-10-23 10:51:11 -0700546 if (portCriterion.port() == PortNumber.ALL) {
547 for (Port port : deviceService.getPorts(deviceId)) {
548 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
549 portnums.add(port.number());
550 }
551 }
552 } else {
553 portnums.add(portCriterion.port());
554 }
Saurav Das4f980082015-11-05 13:39:15 -0800555
Saurav Das822c4e22015-10-23 10:51:11 -0700556 for (PortNumber pnum : portnums) {
Saurav Das4f980082015-11-05 13:39:15 -0800557 // create rest of flowrule
Saurav Das822c4e22015-10-23 10:51:11 -0700558 selector.matchInPort(pnum);
559 FlowRule rule = DefaultFlowRule.builder()
560 .forDevice(deviceId)
561 .withSelector(selector.build())
562 .withTreatment(treatment.build())
563 .withPriority(DEFAULT_PRIORITY)
564 .fromApp(applicationId)
565 .makePermanent()
566 .forTable(VLAN_TABLE).build();
Charles Chan14967c22015-12-07 11:11:50 -0800567
568 if (preSelector != null) {
569 preSelector.matchInPort(pnum);
570 FlowRule preRule = DefaultFlowRule.builder()
571 .forDevice(deviceId)
572 .withSelector(preSelector.build())
573 .withTreatment(preTreatment.build())
574 .withPriority(DEFAULT_PRIORITY)
575 .fromApp(applicationId)
576 .makePermanent()
577 .forTable(VLAN_TABLE).build();
578 rules.add(preRule);
579 }
580
Saurav Das822c4e22015-10-23 10:51:11 -0700581 rules.add(rule);
582 }
583 return rules;
584 }
585
586 /**
587 * Allows routed packets with correct destination MAC to be directed
588 * to unicast-IP routing table or MPLS forwarding table.
Saurav Das822c4e22015-10-23 10:51:11 -0700589 *
590 * @param portCriterion port on device for which this filter is programmed
591 * @param ethCriterion dstMac of device for which is filter is programmed
592 * @param vidCriterion vlan assigned to port, or NONE for untagged
Saurav Das0e99e2b2015-10-28 12:39:42 -0700593 * @param assignedVlan assigned vlan-id for untagged packets
Saurav Das822c4e22015-10-23 10:51:11 -0700594 * @param applicationId for application programming this filter
595 * @return list of FlowRule for port-vlan filters
596
597 */
598 protected List<FlowRule> processEthDstFilter(PortCriterion portCriterion,
599 EthCriterion ethCriterion,
600 VlanIdCriterion vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700601 VlanId assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700602 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800603 // Consider PortNumber.ANY as wildcard. Match ETH_DST only
604 if (portCriterion != null && portCriterion.port() == PortNumber.ANY) {
605 return processEthDstOnlyFilter(ethCriterion, applicationId);
606 }
607
Charles Chan5b9df8d2016-03-28 22:21:40 -0700608 // Multicast MAC
609 if (ethCriterion.mask() != null) {
610 return processMcastEthDstFilter(ethCriterion, applicationId);
611 }
612
Saurav Das822c4e22015-10-23 10:51:11 -0700613 //handling untagged packets via assigned VLAN
614 if (vidCriterion.vlanId() == VlanId.NONE) {
Saurav Das0e99e2b2015-10-28 12:39:42 -0700615 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
Saurav Das822c4e22015-10-23 10:51:11 -0700616 }
617 // ofdpa cannot match on ALL portnumber, so we need to use separate
618 // rules for each port.
Yuta HIGUCHI2dce08a2017-04-20 21:57:48 -0700619 List<PortNumber> portnums = new ArrayList<>();
Saurav Das822c4e22015-10-23 10:51:11 -0700620 if (portCriterion.port() == PortNumber.ALL) {
621 for (Port port : deviceService.getPorts(deviceId)) {
622 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
623 portnums.add(port.number());
624 }
625 }
626 } else {
627 portnums.add(portCriterion.port());
628 }
629
Yuta HIGUCHI2dce08a2017-04-20 21:57:48 -0700630 List<FlowRule> rules = new ArrayList<>();
Saurav Das822c4e22015-10-23 10:51:11 -0700631 for (PortNumber pnum : portnums) {
Charles Chan14967c22015-12-07 11:11:50 -0800632 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vidCriterion.vlanId());
Saurav Das822c4e22015-10-23 10:51:11 -0700633 // for unicast IP packets
634 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
635 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
Saurav Das86d13e82017-04-28 17:03:48 -0700636 if (matchInPortTmacTable()) {
637 selector.matchInPort(pnum);
638 }
Charles Chand1172632017-03-15 17:33:09 -0700639 if (requireVlanExtensions()) {
640 selector.extension(ofdpaMatchVlanVid, deviceId);
641 } else {
642 selector.matchVlanId(vidCriterion.vlanId());
643 }
Saurav Das822c4e22015-10-23 10:51:11 -0700644 selector.matchEthType(Ethernet.TYPE_IPV4);
645 selector.matchEthDst(ethCriterion.mac());
646 treatment.transition(UNICAST_ROUTING_TABLE);
647 FlowRule rule = DefaultFlowRule.builder()
648 .forDevice(deviceId)
649 .withSelector(selector.build())
650 .withTreatment(treatment.build())
651 .withPriority(DEFAULT_PRIORITY)
652 .fromApp(applicationId)
653 .makePermanent()
654 .forTable(TMAC_TABLE).build();
655 rules.add(rule);
656 //for MPLS packets
657 selector = DefaultTrafficSelector.builder();
658 treatment = DefaultTrafficTreatment.builder();
Saurav Das86d13e82017-04-28 17:03:48 -0700659 if (matchInPortTmacTable()) {
660 selector.matchInPort(pnum);
661 }
Charles Chand1172632017-03-15 17:33:09 -0700662 if (requireVlanExtensions()) {
663 selector.extension(ofdpaMatchVlanVid, deviceId);
664 } else {
665 selector.matchVlanId(vidCriterion.vlanId());
666 }
Saurav Das822c4e22015-10-23 10:51:11 -0700667 selector.matchEthType(Ethernet.MPLS_UNICAST);
668 selector.matchEthDst(ethCriterion.mac());
669 treatment.transition(MPLS_TABLE_0);
670 rule = DefaultFlowRule.builder()
671 .forDevice(deviceId)
672 .withSelector(selector.build())
673 .withTreatment(treatment.build())
674 .withPriority(DEFAULT_PRIORITY)
675 .fromApp(applicationId)
676 .makePermanent()
677 .forTable(TMAC_TABLE).build();
678 rules.add(rule);
Pier Ventree0ae7a32016-11-23 09:57:42 -0800679 /*
680 * TMAC rules for IPv6 packets
681 */
682 selector = DefaultTrafficSelector.builder();
683 treatment = DefaultTrafficTreatment.builder();
Saurav Das86d13e82017-04-28 17:03:48 -0700684 if (matchInPortTmacTable()) {
685 selector.matchInPort(pnum);
686 }
Charles Chand1172632017-03-15 17:33:09 -0700687 if (requireVlanExtensions()) {
688 selector.extension(ofdpaMatchVlanVid, deviceId);
689 } else {
690 selector.matchVlanId(vidCriterion.vlanId());
691 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800692 selector.matchEthType(Ethernet.TYPE_IPV6);
693 selector.matchEthDst(ethCriterion.mac());
694 treatment.transition(UNICAST_ROUTING_TABLE);
695 rule = DefaultFlowRule.builder()
696 .forDevice(deviceId)
697 .withSelector(selector.build())
698 .withTreatment(treatment.build())
699 .withPriority(DEFAULT_PRIORITY)
700 .fromApp(applicationId)
701 .makePermanent()
702 .forTable(TMAC_TABLE).build();
703 rules.add(rule);
Saurav Das822c4e22015-10-23 10:51:11 -0700704 }
705 return rules;
706 }
707
Charles Chan5270ed02016-01-30 23:22:37 -0800708 protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700709 ApplicationId applicationId) {
Pier Luigi0e358632017-01-31 09:35:05 -0800710 ImmutableList.Builder<FlowRule> builder = ImmutableList.builder();
711
Charles Chan5270ed02016-01-30 23:22:37 -0800712 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
713 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
714 selector.matchEthType(Ethernet.TYPE_IPV4);
715 selector.matchEthDst(ethCriterion.mac());
716 treatment.transition(UNICAST_ROUTING_TABLE);
717 FlowRule rule = DefaultFlowRule.builder()
718 .forDevice(deviceId)
719 .withSelector(selector.build())
720 .withTreatment(treatment.build())
721 .withPriority(DEFAULT_PRIORITY)
722 .fromApp(applicationId)
723 .makePermanent()
724 .forTable(TMAC_TABLE).build();
Pier Luigi0e358632017-01-31 09:35:05 -0800725 builder.add(rule);
726
727 selector = DefaultTrafficSelector.builder();
728 treatment = DefaultTrafficTreatment.builder();
729 selector.matchEthType(Ethernet.TYPE_IPV6);
730 selector.matchEthDst(ethCriterion.mac());
731 treatment.transition(UNICAST_ROUTING_TABLE);
732 rule = DefaultFlowRule.builder()
733 .forDevice(deviceId)
734 .withSelector(selector.build())
735 .withTreatment(treatment.build())
736 .withPriority(DEFAULT_PRIORITY)
737 .fromApp(applicationId)
738 .makePermanent()
739 .forTable(TMAC_TABLE).build();
740 return builder.add(rule).build();
Charles Chan5270ed02016-01-30 23:22:37 -0800741 }
742
Charles Chan5b9df8d2016-03-28 22:21:40 -0700743 protected List<FlowRule> processMcastEthDstFilter(EthCriterion ethCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700744 ApplicationId applicationId) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700745 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
746 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
747 selector.matchEthType(Ethernet.TYPE_IPV4);
748 selector.matchEthDstMasked(ethCriterion.mac(), ethCriterion.mask());
749 treatment.transition(MULTICAST_ROUTING_TABLE);
750 FlowRule rule = DefaultFlowRule.builder()
751 .forDevice(deviceId)
752 .withSelector(selector.build())
753 .withTreatment(treatment.build())
754 .withPriority(DEFAULT_PRIORITY)
755 .fromApp(applicationId)
756 .makePermanent()
757 .forTable(TMAC_TABLE).build();
758 return ImmutableList.<FlowRule>builder().add(rule).build();
759 }
760
Saurav Das822c4e22015-10-23 10:51:11 -0700761 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
762 switch (fwd.flag()) {
763 case SPECIFIC:
764 return processSpecific(fwd);
765 case VERSATILE:
766 return processVersatile(fwd);
767 default:
768 fail(fwd, ObjectiveError.UNKNOWN);
769 log.warn("Unknown forwarding flag {}", fwd.flag());
770 }
771 return Collections.emptySet();
772 }
773
774 /**
775 * In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the
776 * ACL table.
777 * @param fwd the forwarding objective of type 'versatile'
778 * @return a collection of flow rules to be sent to the switch. An empty
779 * collection may be returned if there is a problem in processing
780 * the flow rule
781 */
Saurav Das52025962016-01-28 22:30:01 -0800782 protected Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das018605f2017-02-18 14:05:44 -0800783 log.debug("Processing versatile forwarding objective:{} in dev:{}",
Saurav Dasd2fded02016-12-02 15:43:47 -0800784 fwd.id(), deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700785
786 EthTypeCriterion ethType =
Saurav Das77b5e902016-01-27 17:01:59 -0800787 (EthTypeCriterion) fwd.selector().getCriterion(Criterion.Type.ETH_TYPE);
Saurav Das822c4e22015-10-23 10:51:11 -0700788 if (ethType == null) {
Saurav Dasd2fded02016-12-02 15:43:47 -0800789 log.error("Versatile forwarding objective:{} must include ethType",
790 fwd.id());
Saurav Das822c4e22015-10-23 10:51:11 -0700791 fail(fwd, ObjectiveError.BADPARAMS);
792 return Collections.emptySet();
793 }
794 if (fwd.nextId() == null && fwd.treatment() == null) {
795 log.error("Forwarding objective {} from {} must contain "
796 + "nextId or Treatment", fwd.selector(), fwd.appId());
Zsolt Haraszti9faab752016-02-17 15:55:20 -0800797 fail(fwd, ObjectiveError.BADPARAMS);
Saurav Das822c4e22015-10-23 10:51:11 -0700798 return Collections.emptySet();
799 }
Saurav Das49cb5a12016-01-16 22:54:07 -0800800
Saurav Das77b5e902016-01-27 17:01:59 -0800801 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
802 fwd.selector().criteria().forEach(criterion -> {
803 if (criterion instanceof VlanIdCriterion) {
804 VlanId vlanId = ((VlanIdCriterion) criterion).vlanId();
805 // ensure that match does not include vlan = NONE as OF-DPA does not
806 // match untagged packets this way in the ACL table.
807 if (vlanId.equals(VlanId.NONE)) {
808 return;
809 }
Charles Chand1172632017-03-15 17:33:09 -0700810 if (requireVlanExtensions()) {
811 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vlanId);
812 sbuilder.extension(ofdpaMatchVlanVid, deviceId);
813 } else {
814 sbuilder.matchVlanId(vlanId);
815 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800816 } else if (criterion instanceof Icmpv6TypeCriterion ||
817 criterion instanceof Icmpv6CodeCriterion) {
818 /*
819 * We silenty discard these criterions, our current
820 * OFDPA platform does not support these matches on
821 * the ACL table.
822 */
823 log.warn("ICMPv6 Type and ICMPv6 Code are not supported");
Saurav Das77b5e902016-01-27 17:01:59 -0800824 } else {
825 sbuilder.add(criterion);
826 }
827 });
828
Saurav Das822c4e22015-10-23 10:51:11 -0700829 // XXX driver does not currently do type checking as per Tables 65-67 in
830 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
Saurav Das49cb5a12016-01-16 22:54:07 -0800831 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
832 if (fwd.treatment() != null) {
833 for (Instruction ins : fwd.treatment().allInstructions()) {
834 if (ins instanceof OutputInstruction) {
835 OutputInstruction o = (OutputInstruction) ins;
836 if (o.port() == PortNumber.CONTROLLER) {
837 ttBuilder.add(o);
838 } else {
839 log.warn("Only allowed treatments in versatile forwarding "
840 + "objectives are punts to the controller");
841 }
842 } else {
843 log.warn("Cannot process instruction in versatile fwd {}", ins);
844 }
Saurav Das822c4e22015-10-23 10:51:11 -0700845 }
Charles Chan2df0e8a2017-01-09 11:45:08 -0800846 if (fwd.treatment().clearedDeferred()) {
847 ttBuilder.wipeDeferred();
848 }
Saurav Das822c4e22015-10-23 10:51:11 -0700849 }
Saurav Das822c4e22015-10-23 10:51:11 -0700850 if (fwd.nextId() != null) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800851 // overide case
852 NextGroup next = getGroupForNextObjective(fwd.nextId());
853 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
854 // we only need the top level group's key to point the flow to it
855 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
856 if (group == null) {
857 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
858 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
859 fail(fwd, ObjectiveError.GROUPMISSING);
860 return Collections.emptySet();
861 }
862 ttBuilder.deferred().group(group.id());
Saurav Das822c4e22015-10-23 10:51:11 -0700863 }
Saurav Das49cb5a12016-01-16 22:54:07 -0800864
865 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
866 .fromApp(fwd.appId())
867 .withPriority(fwd.priority())
868 .forDevice(deviceId)
Saurav Das77b5e902016-01-27 17:01:59 -0800869 .withSelector(sbuilder.build())
Saurav Das49cb5a12016-01-16 22:54:07 -0800870 .withTreatment(ttBuilder.build())
871 .makePermanent()
872 .forTable(ACL_TABLE);
873 return Collections.singletonList(ruleBuilder.build());
Saurav Das822c4e22015-10-23 10:51:11 -0700874 }
875
876 /**
877 * In the OF-DPA 2.0 pipeline, specific forwarding refers to the IP table
Saurav Das8a0732e2015-11-20 15:27:53 -0800878 * (unicast or multicast) or the L2 table (mac + vlan) or the MPLS table.
Saurav Das822c4e22015-10-23 10:51:11 -0700879 *
880 * @param fwd the forwarding objective of type 'specific'
881 * @return a collection of flow rules. Typically there will be only one
882 * for this type of forwarding objective. An empty set may be
883 * returned if there is an issue in processing the objective.
884 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800885 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das25190812016-05-27 13:54:07 -0700886 log.debug("Processing specific fwd objective:{} in dev:{} with next:{}",
Saurav Das4ce45962015-11-24 23:21:05 -0800887 fwd.id(), deviceId, fwd.nextId());
888 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
889 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
890
891 if (isEthTypeObj) {
892 return processEthTypeSpecific(fwd);
893 } else if (isEthDstObj) {
894 return processEthDstSpecific(fwd);
895 } else {
896 log.warn("processSpecific: Unsupported forwarding objective "
897 + "criteria fwd:{} in dev:{}", fwd.nextId(), deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700898 fail(fwd, ObjectiveError.UNSUPPORTED);
899 return Collections.emptySet();
900 }
Saurav Das4ce45962015-11-24 23:21:05 -0800901 }
902
Saurav Das4ce45962015-11-24 23:21:05 -0800903 /**
904 * Handles forwarding rules to the IP and MPLS tables.
905 *
906 * @param fwd the forwarding objective
907 * @return A collection of flow rules, or an empty set
908 */
909 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
Charles Chancad338a2016-09-16 18:03:11 -0700910 return processEthTypeSpecificInternal(fwd, false, ACL_TABLE);
Charles Chanf9e98652016-09-07 16:54:23 -0700911 }
912
913 /**
914 * Internal implementation of processEthTypeSpecific.
915 * <p>
916 * Wildcarded IPv4_DST is not supported in OFDPA i12. Therefore, we break
917 * the rule into 0.0.0.0/1 and 128.0.0.0/1.
918 * The allowDefaultRoute must be set to false for OFDPA i12.
919 * </p>
920 *
921 * @param fwd the forwarding objective
922 * @param allowDefaultRoute allow wildcarded IPv4_DST or not
Ray Milkey0bb1e102016-11-10 14:51:27 -0800923 * @param mplsNextTable next MPLS table
Charles Chanf9e98652016-09-07 16:54:23 -0700924 * @return A collection of flow rules, or an empty set
925 */
926 protected Collection<FlowRule> processEthTypeSpecificInternal(ForwardingObjective fwd,
Charles Chancad338a2016-09-16 18:03:11 -0700927 boolean allowDefaultRoute,
928 int mplsNextTable) {
Saurav Das4ce45962015-11-24 23:21:05 -0800929 TrafficSelector selector = fwd.selector();
930 EthTypeCriterion ethType =
931 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
Flavio Castroe10fa242016-01-15 12:43:51 -0800932 boolean popMpls = false;
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700933 boolean emptyGroup = false;
Charles Chan188ebf52015-12-23 00:15:11 -0800934 int forTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800935 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
Charles Chan14967c22015-12-07 11:11:50 -0800936 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
Flavio Castroe10fa242016-01-15 12:43:51 -0800937 TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder();
Charles Chan14967c22015-12-07 11:11:50 -0800938
Saurav Das8a0732e2015-11-20 15:27:53 -0800939 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Pier Luigi3bfe32c2017-01-30 09:47:36 -0800940 if (buildIpv4Selector(filteredSelector, complementarySelector, fwd, allowDefaultRoute) < 0) {
941 return Collections.emptyList();
942 }
943 // We need to set properly the next table
Flavio Castroe10fa242016-01-15 12:43:51 -0800944 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700945 if (ipv4Dst.isMulticast()) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700946 forTableId = MULTICAST_ROUTING_TABLE;
Flavio Castroe10fa242016-01-15 12:43:51 -0800947 } else {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700948 forTableId = UNICAST_ROUTING_TABLE;
Flavio Castroe10fa242016-01-15 12:43:51 -0800949 }
Charles Chan14967c22015-12-07 11:11:50 -0800950 if (fwd.treatment() != null) {
951 for (Instruction instr : fwd.treatment().allInstructions()) {
952 if (instr instanceof L3ModificationInstruction &&
953 ((L3ModificationInstruction) instr).subtype() == L3SubType.DEC_TTL) {
Saurav Das9c705342017-01-06 11:36:01 -0800954 // XXX decrementing IP ttl is done automatically for routing, this
955 // action is ignored or rejected in ofdpa as it is not fully implemented
956 //tb.deferred().add(instr);
Charles Chan14967c22015-12-07 11:11:50 -0800957 }
958 }
959 }
960
Pier Ventree0ae7a32016-11-23 09:57:42 -0800961 } else if (ethType.ethType().toShort() == Ethernet.TYPE_IPV6) {
962 if (buildIpv6Selector(filteredSelector, fwd) < 0) {
963 return Collections.emptyList();
964 }
965 forTableId = UNICAST_ROUTING_TABLE;
966 if (fwd.treatment() != null) {
967 for (Instruction instr : fwd.treatment().allInstructions()) {
968 if (instr instanceof L3ModificationInstruction &&
969 ((L3ModificationInstruction) instr).subtype() == L3SubType.DEC_TTL) {
970 // XXX decrementing IP ttl is done automatically for routing, this
971 // action is ignored or rejected in ofdpa as it is not fully implemented
972 //tb.deferred().add(instr);
973 }
974 }
975 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800976 } else {
977 filteredSelector
978 .matchEthType(Ethernet.MPLS_UNICAST)
979 .matchMplsLabel(((MplsCriterion)
980 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
981 MplsBosCriterion bos = (MplsBosCriterion) selector
Pier Ventre140a8942016-11-02 07:26:38 -0700982 .getCriterion(MPLS_BOS);
Saurav Das8a0732e2015-11-20 15:27:53 -0800983 if (bos != null) {
984 filteredSelector.matchMplsBos(bos.mplsBos());
985 }
986 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800987 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
988 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700989
Charles Chan14967c22015-12-07 11:11:50 -0800990 if (fwd.treatment() != null) {
991 for (Instruction instr : fwd.treatment().allInstructions()) {
992 if (instr instanceof L2ModificationInstruction &&
993 ((L2ModificationInstruction) instr).subtype() == L2SubType.MPLS_POP) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800994 popMpls = true;
Pier Luigi3bfe32c2017-01-30 09:47:36 -0800995 // OF-DPA does not pop in MPLS table in some cases. For the L3 VPN, it requires
Saurav Das9c705342017-01-06 11:36:01 -0800996 // setting the MPLS_TYPE so pop can happen down the pipeline
Pier Luigi3bfe32c2017-01-30 09:47:36 -0800997 if (mplsNextTable == MPLS_TYPE_TABLE && isNotMplsBos(selector)) {
998 tb.immediate().popMpls();
999 }
Charles Chan14967c22015-12-07 11:11:50 -08001000 }
1001 if (instr instanceof L3ModificationInstruction &&
1002 ((L3ModificationInstruction) instr).subtype() == L3SubType.DEC_TTL) {
1003 // FIXME Should modify the app to send the correct DEC_MPLS_TTL instruction
1004 tb.immediate().decMplsTtl();
1005 }
1006 if (instr instanceof L3ModificationInstruction &&
1007 ((L3ModificationInstruction) instr).subtype() == L3SubType.TTL_IN) {
1008 tb.immediate().add(instr);
1009 }
Saurav Das8a0732e2015-11-20 15:27:53 -08001010 }
1011 }
1012 }
Saurav Das822c4e22015-10-23 10:51:11 -07001013
1014 if (fwd.nextId() != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -08001015 if (forTableId == MPLS_TABLE_1 && !popMpls) {
1016 log.warn("SR CONTINUE case cannot be handled as MPLS ECMP "
Saurav Das25190812016-05-27 13:54:07 -07001017 + "is not implemented in OF-DPA yet. Aborting this flow {} -> next:{}"
1018 + "in this device {}", fwd.id(), fwd.nextId(), deviceId);
Pier Ventree0ae7a32016-11-23 09:57:42 -08001019 // XXX We could convert to forwarding to a single-port, via a MPLS interface,
1020 // or a MPLS SWAP (with-same) but that would have to be handled in the next-objective.
1021 // Also the pop-mpls logic used here won't work in non-BoS case.
Saurav Das4ce45962015-11-24 23:21:05 -08001022 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Saurav Das8a0732e2015-11-20 15:27:53 -08001023 return Collections.emptySet();
1024 }
1025
Saurav Das423fe2b2015-12-04 10:52:59 -08001026 NextGroup next = getGroupForNextObjective(fwd.nextId());
1027 if (next != null) {
1028 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1029 // we only need the top level group's key to point the flow to it
1030 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
Pier Ventre140a8942016-11-02 07:26:38 -07001031 if (isNotMplsBos(selector) && group.type().equals(HASHED)) {
1032 log.warn("SR CONTINUE case cannot be handled as MPLS ECMP "
1033 + "is not implemented in OF-DPA yet. Aborting this flow {} -> next:{}"
1034 + "in this device {}", fwd.id(), fwd.nextId(), deviceId);
1035 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
1036 return Collections.emptySet();
1037 }
Saurav Das423fe2b2015-12-04 10:52:59 -08001038 if (group == null) {
1039 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1040 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
1041 fail(fwd, ObjectiveError.GROUPMISSING);
1042 return Collections.emptySet();
1043 }
1044 tb.deferred().group(group.id());
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001045 // check if group is empty
1046 if (gkeys.size() == 1 && gkeys.get(0).size() == 1) {
1047 log.warn("Found empty group 0x{} in dev:{} .. will retry fwd:{}",
1048 Integer.toHexString(group.id().id()), deviceId, fwd.id());
1049 emptyGroup = true;
1050 }
Saurav Das25190812016-05-27 13:54:07 -07001051 } else {
1052 log.warn("Cannot find group for nextId:{} in dev:{}. Aborting fwd:{}",
1053 fwd.nextId(), deviceId, fwd.id());
1054 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
1055 return Collections.emptySet();
Saurav Das822c4e22015-10-23 10:51:11 -07001056 }
Saurav Das822c4e22015-10-23 10:51:11 -07001057 }
Charles Chancad338a2016-09-16 18:03:11 -07001058
1059 if (forTableId == MPLS_TABLE_1) {
Pier Ventre140a8942016-11-02 07:26:38 -07001060 if (mplsNextTable == MPLS_L3_TYPE_TABLE) {
Charles Chancad338a2016-09-16 18:03:11 -07001061 Ofdpa3SetMplsType setMplsType = new Ofdpa3SetMplsType(Ofdpa3MplsType.L3_PHP);
Saurav Das9c705342017-01-06 11:36:01 -08001062 // set mpls type as apply_action
1063 tb.immediate().extension(setMplsType, deviceId);
Charles Chancad338a2016-09-16 18:03:11 -07001064 }
1065 tb.transition(mplsNextTable);
1066 } else {
1067 tb.transition(ACL_TABLE);
1068 }
1069
Saurav Das822c4e22015-10-23 10:51:11 -07001070 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
1071 .fromApp(fwd.appId())
1072 .withPriority(fwd.priority())
1073 .forDevice(deviceId)
Saurav Das8a0732e2015-11-20 15:27:53 -08001074 .withSelector(filteredSelector.build())
1075 .withTreatment(tb.build())
1076 .forTable(forTableId);
Saurav Das822c4e22015-10-23 10:51:11 -07001077
1078 if (fwd.permanent()) {
1079 ruleBuilder.makePermanent();
1080 } else {
1081 ruleBuilder.makeTemporary(fwd.timeout());
1082 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001083 Collection<FlowRule> flowRuleCollection = new ArrayList<>();
1084 flowRuleCollection.add(ruleBuilder.build());
Pier Luigi3bfe32c2017-01-30 09:47:36 -08001085 if (!allowDefaultRoute) {
Pier Ventree0ae7a32016-11-23 09:57:42 -08001086 flowRuleCollection.add(
1087 defaultRoute(fwd, complementarySelector, forTableId, tb)
1088 );
Flavio Castroe10fa242016-01-15 12:43:51 -08001089 log.debug("Default rule 0.0.0.0/0 is being installed two rules");
1090 }
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001091 // XXX retrying flows may be necessary due to bug CORD-554
1092 if (emptyGroup) {
1093 executorService.schedule(new RetryFlows(fwd, flowRuleCollection),
1094 RETRY_MS, TimeUnit.MILLISECONDS);
1095 }
Flavio Castroe10fa242016-01-15 12:43:51 -08001096 return flowRuleCollection;
Saurav Das822c4e22015-10-23 10:51:11 -07001097 }
1098
Pier Luigi3bfe32c2017-01-30 09:47:36 -08001099 protected int buildIpv4Selector(TrafficSelector.Builder builderToUpdate,
1100 TrafficSelector.Builder extBuilder,
1101 ForwardingObjective fwd,
1102 boolean allowDefaultRoute) {
1103 TrafficSelector selector = fwd.selector();
1104
1105 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
1106 if (ipv4Dst.isMulticast()) {
1107 if (ipv4Dst.prefixLength() != 32) {
1108 log.warn("Multicast specific forwarding objective can only be /32");
1109 fail(fwd, ObjectiveError.BADPARAMS);
1110 return -1;
1111 }
1112 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
1113 if (assignedVlan == null) {
1114 log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
1115 fail(fwd, ObjectiveError.BADPARAMS);
1116 return -1;
1117 }
Charles Chand1172632017-03-15 17:33:09 -07001118 if (requireVlanExtensions()) {
1119 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan);
1120 builderToUpdate.extension(ofdpaMatchVlanVid, deviceId);
1121 } else {
1122 builderToUpdate.matchVlanId(assignedVlan);
1123 }
Pier Luigi3bfe32c2017-01-30 09:47:36 -08001124 builderToUpdate.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
1125 log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
1126 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
1127 } else {
1128 if (ipv4Dst.prefixLength() == 0) {
1129 if (allowDefaultRoute) {
1130 // The entire IPV4_DST field is wildcarded intentionally
1131 builderToUpdate.matchEthType(Ethernet.TYPE_IPV4);
1132 } else {
1133 // NOTE: The switch does not support matching 0.0.0.0/0
1134 // Split it into 0.0.0.0/1 and 128.0.0.0/1
1135 builderToUpdate.matchEthType(Ethernet.TYPE_IPV4)
1136 .matchIPDst(IpPrefix.valueOf("0.0.0.0/1"));
1137 extBuilder.matchEthType(Ethernet.TYPE_IPV4)
1138 .matchIPDst(IpPrefix.valueOf("128.0.0.0/1"));
1139 }
1140 } else {
1141 builderToUpdate.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
1142 }
1143 log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
1144 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
1145 }
1146 return 0;
1147 }
1148
1149 /**
1150 * Helper method to build Ipv6 selector using the selector provided by
1151 * a forwarding objective.
1152 *
1153 * @param builderToUpdate the builder to update
1154 * @param fwd the selector to read
1155 * @return 0 if the update ends correctly. -1 if the matches
1156 * are not yet supported
1157 */
1158 protected int buildIpv6Selector(TrafficSelector.Builder builderToUpdate,
1159 ForwardingObjective fwd) {
1160
1161 TrafficSelector selector = fwd.selector();
1162
1163 IpPrefix ipv6Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV6_DST)).ip();
1164 if (ipv6Dst.isMulticast()) {
1165 log.warn("IPv6 Multicast is currently not supported");
1166 fail(fwd, ObjectiveError.BADPARAMS);
1167 return -1;
1168 }
1169 if (ipv6Dst.prefixLength() != 0) {
1170 builderToUpdate.matchIPv6Dst(ipv6Dst);
1171 }
1172 builderToUpdate.matchEthType(Ethernet.TYPE_IPV6);
1173 log.debug("processing IPv6 unicast specific forwarding objective {} -> next:{}"
1174 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
1175 return 0;
1176 }
1177
Pier Ventree0ae7a32016-11-23 09:57:42 -08001178 protected FlowRule defaultRoute(ForwardingObjective fwd,
1179 TrafficSelector.Builder complementarySelector,
1180 int forTableId,
1181 TrafficTreatment.Builder tb) {
1182 FlowRule.Builder rule = DefaultFlowRule.builder()
1183 .fromApp(fwd.appId())
1184 .withPriority(fwd.priority())
1185 .forDevice(deviceId)
1186 .withSelector(complementarySelector.build())
1187 .withTreatment(tb.build())
1188 .forTable(forTableId);
1189 if (fwd.permanent()) {
1190 rule.makePermanent();
1191 } else {
1192 rule.makeTemporary(fwd.timeout());
1193 }
1194 return rule.build();
1195 }
1196
Saurav Das4ce45962015-11-24 23:21:05 -08001197 /**
1198 * Handles forwarding rules to the L2 bridging table. Flow actions are not
1199 * allowed in the bridging table - instead we use L2 Interface group or
1200 * L2 flood group
1201 *
1202 * @param fwd the forwarding objective
1203 * @return A collection of flow rules, or an empty set
1204 */
1205 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
1206 List<FlowRule> rules = new ArrayList<>();
1207
1208 // Build filtered selector
1209 TrafficSelector selector = fwd.selector();
1210 EthCriterion ethCriterion = (EthCriterion) selector
1211 .getCriterion(Criterion.Type.ETH_DST);
1212 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
1213 .getCriterion(Criterion.Type.VLAN_VID);
1214
1215 if (vlanIdCriterion == null) {
1216 log.warn("Forwarding objective for bridging requires vlan. Not "
1217 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
1218 fail(fwd, ObjectiveError.BADPARAMS);
1219 return Collections.emptySet();
1220 }
1221
1222 TrafficSelector.Builder filteredSelectorBuilder =
1223 DefaultTrafficSelector.builder();
Yi Tseng3a77b4f2017-02-06 15:02:17 -08001224
1225 if (!ethCriterion.mac().equals(NONE) &&
1226 !ethCriterion.mac().equals(BROADCAST)) {
Saurav Das4ce45962015-11-24 23:21:05 -08001227 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
1228 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
1229 fwd.id(), fwd.nextId(), deviceId);
1230 } else {
Yi Tseng3a77b4f2017-02-06 15:02:17 -08001231 // Use wildcard DST_MAC if the MacAddress is None or Broadcast
Saurav Das4ce45962015-11-24 23:21:05 -08001232 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
1233 + "in dev:{} for vlan:{}",
1234 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
1235 }
Charles Chand1172632017-03-15 17:33:09 -07001236 if (requireVlanExtensions()) {
1237 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vlanIdCriterion.vlanId());
1238 filteredSelectorBuilder.extension(ofdpaMatchVlanVid, deviceId);
1239 } else {
1240 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
1241 }
Saurav Das4ce45962015-11-24 23:21:05 -08001242 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
1243
1244 if (fwd.treatment() != null) {
1245 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
1246 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
1247 }
1248
1249 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1250 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -08001251 NextGroup next = getGroupForNextObjective(fwd.nextId());
Saurav Das4ce45962015-11-24 23:21:05 -08001252 if (next != null) {
1253 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1254 // we only need the top level group's key to point the flow to it
1255 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1256 if (group != null) {
1257 treatmentBuilder.deferred().group(group.id());
1258 } else {
1259 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1260 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
1261 fail(fwd, ObjectiveError.GROUPMISSING);
1262 return Collections.emptySet();
1263 }
1264 }
1265 }
1266 treatmentBuilder.immediate().transition(ACL_TABLE);
1267 TrafficTreatment filteredTreatment = treatmentBuilder.build();
1268
1269 // Build bridging table entries
1270 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
1271 flowRuleBuilder.fromApp(fwd.appId())
1272 .withPriority(fwd.priority())
1273 .forDevice(deviceId)
1274 .withSelector(filteredSelector)
1275 .withTreatment(filteredTreatment)
1276 .forTable(BRIDGING_TABLE);
1277 if (fwd.permanent()) {
1278 flowRuleBuilder.makePermanent();
1279 } else {
1280 flowRuleBuilder.makeTemporary(fwd.timeout());
1281 }
1282 rules.add(flowRuleBuilder.build());
1283 return rules;
1284 }
1285
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001286 //////////////////////////////////////
1287 // Helper Methods and Classes
1288 //////////////////////////////////////
1289
1290 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
1291 TrafficSelector selector = fwd.selector();
1292 EthTypeCriterion ethType = (EthTypeCriterion) selector
1293 .getCriterion(Criterion.Type.ETH_TYPE);
1294 return !((ethType == null) ||
1295 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
Pier Ventree0ae7a32016-11-23 09:57:42 -08001296 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST)) &&
1297 (ethType.ethType().toShort() != Ethernet.TYPE_IPV6));
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001298 }
1299
1300 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
1301 TrafficSelector selector = fwd.selector();
1302 EthCriterion ethDst = (EthCriterion) selector
1303 .getCriterion(Criterion.Type.ETH_DST);
1304 VlanIdCriterion vlanId = (VlanIdCriterion) selector
1305 .getCriterion(Criterion.Type.VLAN_VID);
1306 return !(ethDst == null && vlanId == null);
1307 }
1308
Saurav Das423fe2b2015-12-04 10:52:59 -08001309 protected NextGroup getGroupForNextObjective(Integer nextId) {
1310 NextGroup next = flowObjectiveStore.getNextGroup(nextId);
1311 if (next != null) {
1312 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1313 if (gkeys != null && !gkeys.isEmpty()) {
1314 return next;
1315 } else {
1316 log.warn("Empty next group found in FlowObjective store for "
1317 + "next-id:{} in dev:{}", nextId, deviceId);
1318 }
1319 } else {
1320 log.warn("next-id {} not found in Flow objective store for dev:{}",
1321 nextId, deviceId);
1322 }
1323 return null;
1324 }
1325
Charles Chan188ebf52015-12-23 00:15:11 -08001326 protected static void pass(Objective obj) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001327 obj.context().ifPresent(context -> context.onSuccess(obj));
Saurav Das822c4e22015-10-23 10:51:11 -07001328 }
1329
Charles Chan188ebf52015-12-23 00:15:11 -08001330 protected static void fail(Objective obj, ObjectiveError error) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001331 obj.context().ifPresent(context -> context.onError(obj, error));
Saurav Das822c4e22015-10-23 10:51:11 -07001332 }
Saurav Das24431192016-03-07 19:13:00 -08001333
Saurav Das24431192016-03-07 19:13:00 -08001334 @Override
1335 public List<String> getNextMappings(NextGroup nextGroup) {
1336 List<String> mappings = new ArrayList<>();
1337 List<Deque<GroupKey>> gkeys = appKryo.deserialize(nextGroup.data());
1338 for (Deque<GroupKey> gkd : gkeys) {
1339 Group lastGroup = null;
Yuta HIGUCHI2dce08a2017-04-20 21:57:48 -07001340 StringBuilder gchain = new StringBuilder();
Saurav Das24431192016-03-07 19:13:00 -08001341 for (GroupKey gk : gkd) {
1342 Group g = groupService.getGroup(deviceId, gk);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001343 if (g == null) {
Saurav Das25190812016-05-27 13:54:07 -07001344 gchain.append(" NoGrp").append(" -->");
Saurav Das8be4e3a2016-03-11 17:19:07 -08001345 continue;
1346 }
1347 gchain.append(" 0x").append(Integer.toHexString(g.id().id()))
1348 .append(" -->");
Saurav Das24431192016-03-07 19:13:00 -08001349 lastGroup = g;
1350 }
1351 // add port information for last group in group-chain
Yuta HIGUCHI2dce08a2017-04-20 21:57:48 -07001352 List<Instruction> lastGroupIns = new ArrayList<>();
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001353 if (lastGroup != null && !lastGroup.buckets().buckets().isEmpty()) {
Saurav Das25190812016-05-27 13:54:07 -07001354 lastGroupIns = lastGroup.buckets().buckets().get(0)
1355 .treatment().allInstructions();
1356 }
1357 for (Instruction i: lastGroupIns) {
Saurav Das24431192016-03-07 19:13:00 -08001358 if (i instanceof OutputInstruction) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001359 gchain.append(" port:").append(((OutputInstruction) i).port());
Saurav Das24431192016-03-07 19:13:00 -08001360 }
1361 }
Saurav Das8be4e3a2016-03-11 17:19:07 -08001362 mappings.add(gchain.toString());
Saurav Das24431192016-03-07 19:13:00 -08001363 }
1364 return mappings;
1365 }
Charles Chan5b9df8d2016-03-28 22:21:40 -07001366
Pier Ventre140a8942016-11-02 07:26:38 -07001367 static boolean isMplsBos(TrafficSelector selector) {
1368 MplsBosCriterion bosCriterion = (MplsBosCriterion) selector.getCriterion(MPLS_BOS);
1369 return bosCriterion != null && bosCriterion.mplsBos();
1370 }
1371
1372 static boolean isNotMplsBos(TrafficSelector selector) {
1373 MplsBosCriterion bosCriterion = (MplsBosCriterion) selector.getCriterion(MPLS_BOS);
1374 return bosCriterion != null && !bosCriterion.mplsBos();
1375 }
1376
Charles Chan5b9df8d2016-03-28 22:21:40 -07001377 protected static VlanId readVlanFromSelector(TrafficSelector selector) {
Saurav Das59232cf2016-04-27 18:35:50 -07001378 if (selector == null) {
1379 return null;
1380 }
Charles Chan5b9df8d2016-03-28 22:21:40 -07001381 Criterion criterion = selector.getCriterion(Criterion.Type.VLAN_VID);
1382 return (criterion == null)
1383 ? null : ((VlanIdCriterion) criterion).vlanId();
1384 }
1385
1386 protected static IpPrefix readIpDstFromSelector(TrafficSelector selector) {
Saurav Das59232cf2016-04-27 18:35:50 -07001387 if (selector == null) {
1388 return null;
1389 }
Charles Chan5b9df8d2016-03-28 22:21:40 -07001390 Criterion criterion = selector.getCriterion(Criterion.Type.IPV4_DST);
1391 return (criterion == null) ? null : ((IPCriterion) criterion).ip();
1392 }
Charles Chand55e84d2016-03-30 17:54:24 -07001393
1394 private static VlanId readVlanFromTreatment(TrafficTreatment treatment) {
Saurav Das59232cf2016-04-27 18:35:50 -07001395 if (treatment == null) {
1396 return null;
1397 }
Charles Chand55e84d2016-03-30 17:54:24 -07001398 for (Instruction i : treatment.allInstructions()) {
1399 if (i instanceof ModVlanIdInstruction) {
1400 return ((ModVlanIdInstruction) i).vlanId();
1401 }
1402 }
1403 return null;
1404 }
Saurav Das1ce0a7b2016-10-21 14:06:29 -07001405
1406 /**
1407 * Utility class that retries sending flows a fixed number of times, even if
1408 * some of the attempts are successful. Used only for forwarding objectives.
1409 */
1410 protected final class RetryFlows implements Runnable {
1411 int attempts = MAX_RETRY_ATTEMPTS;
1412 private Collection<FlowRule> retryFlows;
1413 private ForwardingObjective fwd;
1414
1415 RetryFlows(ForwardingObjective fwd, Collection<FlowRule> retryFlows) {
1416 this.fwd = fwd;
1417 this.retryFlows = retryFlows;
1418 }
1419
1420 @Override
1421 public void run() {
1422 log.info("RETRY FLOWS ATTEMPT# {} for fwd:{} rules:{}",
1423 MAX_RETRY_ATTEMPTS - attempts, fwd.id(), retryFlows.size());
1424 sendForward(fwd, retryFlows);
1425 if (--attempts > 0) {
1426 executorService.schedule(this, RETRY_MS, TimeUnit.MILLISECONDS);
1427 }
1428 }
1429 }
1430
Saurav Das822c4e22015-10-23 10:51:11 -07001431}