blob: be35026958d275da87a488a4ddd81abefaa2c41b [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 */
16package org.onosproject.driver.pipeline;
17
Saurav Das822c4e22015-10-23 10:51:11 -070018import static org.slf4j.LoggerFactory.getLogger;
19
Saurav Das8a0732e2015-11-20 15:27:53 -080020import java.util.ArrayDeque;
Saurav Das822c4e22015-10-23 10:51:11 -070021import java.util.ArrayList;
22import java.util.Collection;
23import java.util.Collections;
Saurav Das8a0732e2015-11-20 15:27:53 -080024import java.util.Deque;
Saurav Das822c4e22015-10-23 10:51:11 -070025import java.util.List;
Sho SHIMIZU45906042016-01-13 23:05:54 -080026import java.util.Objects;
Saurav Das822c4e22015-10-23 10:51:11 -070027import java.util.Set;
28import java.util.concurrent.ConcurrentHashMap;
Saurav Das822c4e22015-10-23 10:51:11 -070029
Charles Chan5270ed02016-01-30 23:22:37 -080030import com.google.common.collect.ImmutableList;
Charles Chan5b9df8d2016-03-28 22:21:40 -070031import com.google.common.collect.ImmutableSet;
Saurav Das822c4e22015-10-23 10:51:11 -070032import org.onlab.osgi.ServiceDirectory;
Saurav Das822c4e22015-10-23 10:51:11 -070033import org.onlab.packet.Ethernet;
Charles Chan14967c22015-12-07 11:11:50 -080034import org.onlab.packet.IpPrefix;
Saurav Das822c4e22015-10-23 10:51:11 -070035import org.onlab.packet.MacAddress;
Saurav Das822c4e22015-10-23 10:51:11 -070036import org.onlab.packet.VlanId;
37import org.onlab.util.KryoNamespace;
38import org.onosproject.core.ApplicationId;
39import org.onosproject.core.CoreService;
Charles Chan14967c22015-12-07 11:11:50 -080040import org.onosproject.driver.extensions.OfdpaMatchVlanVid;
41import org.onosproject.driver.extensions.OfdpaSetVlanVid;
Saurav Das822c4e22015-10-23 10:51:11 -070042import org.onosproject.net.DeviceId;
43import org.onosproject.net.Port;
44import org.onosproject.net.PortNumber;
45import org.onosproject.net.behaviour.NextGroup;
46import org.onosproject.net.behaviour.Pipeliner;
47import org.onosproject.net.behaviour.PipelinerContext;
48import org.onosproject.net.device.DeviceService;
49import org.onosproject.net.driver.AbstractHandlerBehaviour;
50import org.onosproject.net.flow.DefaultFlowRule;
51import org.onosproject.net.flow.DefaultTrafficSelector;
52import org.onosproject.net.flow.DefaultTrafficTreatment;
53import org.onosproject.net.flow.FlowRule;
54import org.onosproject.net.flow.FlowRuleOperations;
55import org.onosproject.net.flow.FlowRuleOperationsContext;
56import org.onosproject.net.flow.FlowRuleService;
57import org.onosproject.net.flow.TrafficSelector;
58import org.onosproject.net.flow.TrafficTreatment;
59import org.onosproject.net.flow.criteria.Criteria;
60import org.onosproject.net.flow.criteria.Criterion;
61import org.onosproject.net.flow.criteria.EthCriterion;
62import org.onosproject.net.flow.criteria.EthTypeCriterion;
Charles Chan14967c22015-12-07 11:11:50 -080063import org.onosproject.net.flow.criteria.ExtensionCriterion;
Saurav Das822c4e22015-10-23 10:51:11 -070064import org.onosproject.net.flow.criteria.IPCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080065import org.onosproject.net.flow.criteria.MplsBosCriterion;
66import org.onosproject.net.flow.criteria.MplsCriterion;
Saurav Das822c4e22015-10-23 10:51:11 -070067import org.onosproject.net.flow.criteria.PortCriterion;
68import org.onosproject.net.flow.criteria.VlanIdCriterion;
69import org.onosproject.net.flow.instructions.Instruction;
70import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
71import org.onosproject.net.flow.instructions.L2ModificationInstruction;
Saurav Das8a0732e2015-11-20 15:27:53 -080072import org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType;
Saurav Das822c4e22015-10-23 10:51:11 -070073import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Charles Chan14967c22015-12-07 11:11:50 -080074import org.onosproject.net.flow.instructions.L3ModificationInstruction;
75import org.onosproject.net.flow.instructions.L3ModificationInstruction.L3SubType;
Saurav Das822c4e22015-10-23 10:51:11 -070076import org.onosproject.net.flowobjective.FilteringObjective;
77import org.onosproject.net.flowobjective.FlowObjectiveStore;
78import org.onosproject.net.flowobjective.ForwardingObjective;
79import org.onosproject.net.flowobjective.NextObjective;
80import org.onosproject.net.flowobjective.Objective;
81import org.onosproject.net.flowobjective.ObjectiveError;
Saurav Das822c4e22015-10-23 10:51:11 -070082import org.onosproject.net.group.DefaultGroupKey;
83import org.onosproject.net.group.Group;
Saurav Das822c4e22015-10-23 10:51:11 -070084import org.onosproject.net.group.GroupKey;
Saurav Das822c4e22015-10-23 10:51:11 -070085import org.onosproject.net.group.GroupService;
Saurav Das822c4e22015-10-23 10:51:11 -070086import org.onosproject.store.serializers.KryoNamespaces;
87import org.slf4j.Logger;
88
Saurav Das822c4e22015-10-23 10:51:11 -070089/**
90 * Driver for Broadcom's OF-DPA v2.0 TTP.
91 *
92 */
Charles Chan361154b2016-03-24 10:23:39 -070093public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeliner {
Saurav Das822c4e22015-10-23 10:51:11 -070094 protected static final int PORT_TABLE = 0;
95 protected static final int VLAN_TABLE = 10;
96 protected static final int TMAC_TABLE = 20;
97 protected static final int UNICAST_ROUTING_TABLE = 30;
98 protected static final int MULTICAST_ROUTING_TABLE = 40;
99 protected static final int MPLS_TABLE_0 = 23;
100 protected static final int MPLS_TABLE_1 = 24;
101 protected static final int BRIDGING_TABLE = 50;
102 protected static final int ACL_TABLE = 60;
103 protected static final int MAC_LEARNING_TABLE = 254;
104 protected static final long OFPP_MAX = 0xffffff00L;
105
Saurav Das52025962016-01-28 22:30:01 -0800106 protected static final int HIGHEST_PRIORITY = 0xffff;
Saurav Das2857f382015-11-03 14:39:27 -0800107 protected static final int DEFAULT_PRIORITY = 0x8000;
Saurav Das822c4e22015-10-23 10:51:11 -0700108 protected static final int LOWEST_PRIORITY = 0x0;
109
Saurav Das822c4e22015-10-23 10:51:11 -0700110 private final Logger log = getLogger(getClass());
Charles Chan425854b2016-04-11 15:32:12 -0700111 protected ServiceDirectory serviceDirectory;
Saurav Das822c4e22015-10-23 10:51:11 -0700112 protected FlowRuleService flowRuleService;
Charles Chan425854b2016-04-11 15:32:12 -0700113 protected CoreService coreService;
Saurav Das8a0732e2015-11-20 15:27:53 -0800114 protected GroupService groupService;
115 protected FlowObjectiveStore flowObjectiveStore;
Saurav Das822c4e22015-10-23 10:51:11 -0700116 protected DeviceId deviceId;
117 protected ApplicationId driverId;
Saurav Das822c4e22015-10-23 10:51:11 -0700118 protected DeviceService deviceService;
Charles Chan188ebf52015-12-23 00:15:11 -0800119 protected static KryoNamespace appKryo = new KryoNamespace.Builder()
Saurav Das822c4e22015-10-23 10:51:11 -0700120 .register(KryoNamespaces.API)
121 .register(GroupKey.class)
122 .register(DefaultGroupKey.class)
Charles Chan361154b2016-03-24 10:23:39 -0700123 .register(Ofdpa2GroupHandler.OfdpaNextGroup.class)
Saurav Das822c4e22015-10-23 10:51:11 -0700124 .register(byte[].class)
Saurav Das8a0732e2015-11-20 15:27:53 -0800125 .register(ArrayDeque.class)
Saurav Das822c4e22015-10-23 10:51:11 -0700126 .build();
127
Charles Chan425854b2016-04-11 15:32:12 -0700128 protected Ofdpa2GroupHandler groupHandler;
Saurav Das822c4e22015-10-23 10:51:11 -0700129
Saurav Das52025962016-01-28 22:30:01 -0800130 protected Set<IPCriterion> sentIpFilters = Collections.newSetFromMap(
Charles Chan188ebf52015-12-23 00:15:11 -0800131 new ConcurrentHashMap<>());
Saurav Das4f980082015-11-05 13:39:15 -0800132
Saurav Das822c4e22015-10-23 10:51:11 -0700133 @Override
134 public void init(DeviceId deviceId, PipelinerContext context) {
Saurav Das822c4e22015-10-23 10:51:11 -0700135 this.deviceId = deviceId;
136
Charles Chan188ebf52015-12-23 00:15:11 -0800137 // Initialize OFDPA group handler
Charles Chan425854b2016-04-11 15:32:12 -0700138 groupHandler = new Ofdpa2GroupHandler();
139 groupHandler.init(deviceId, context);
Saurav Das822c4e22015-10-23 10:51:11 -0700140
Charles Chan425854b2016-04-11 15:32:12 -0700141 serviceDirectory = context.directory();
Saurav Das822c4e22015-10-23 10:51:11 -0700142 coreService = serviceDirectory.get(CoreService.class);
143 flowRuleService = serviceDirectory.get(FlowRuleService.class);
144 groupService = serviceDirectory.get(GroupService.class);
145 flowObjectiveStore = context.store();
Saurav Das822c4e22015-10-23 10:51:11 -0700146 deviceService = serviceDirectory.get(DeviceService.class);
Saurav Das822c4e22015-10-23 10:51:11 -0700147
148 driverId = coreService.registerApplication(
Charles Chan425854b2016-04-11 15:32:12 -0700149 "org.onosproject.driver.Ofdpa2Pipeline");
Saurav Das822c4e22015-10-23 10:51:11 -0700150
Saurav Das822c4e22015-10-23 10:51:11 -0700151 initializePipeline();
Saurav Das822c4e22015-10-23 10:51:11 -0700152 }
153
154 protected void initializePipeline() {
Charles Chan188ebf52015-12-23 00:15:11 -0800155 // OF-DPA does not require initializing the pipeline as it puts default
156 // rules automatically in the hardware. However emulation of OFDPA in
157 // software switches does require table-miss-entries.
Saurav Das822c4e22015-10-23 10:51:11 -0700158 }
159
160 //////////////////////////////////////
161 // Flow Objectives
162 //////////////////////////////////////
163
164 @Override
165 public void filter(FilteringObjective filteringObjective) {
166 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
167 processFilter(filteringObjective,
168 filteringObjective.op() == Objective.Operation.ADD,
169 filteringObjective.appId());
170 } else {
171 // Note that packets that don't match the PERMIT filter are
172 // automatically denied. The DENY filter is used to deny packets
173 // that are otherwise permitted by the PERMIT filter.
174 // Use ACL table flow rules here for DENY filtering objectives
175 log.debug("filter objective other than PERMIT currently not supported");
176 fail(filteringObjective, ObjectiveError.UNSUPPORTED);
177 }
178 }
179
180 @Override
181 public void forward(ForwardingObjective fwd) {
182 Collection<FlowRule> rules;
183 FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
184
185 rules = processForward(fwd);
186 switch (fwd.op()) {
187 case ADD:
188 rules.stream()
Sho SHIMIZU45906042016-01-13 23:05:54 -0800189 .filter(Objects::nonNull)
Saurav Das822c4e22015-10-23 10:51:11 -0700190 .forEach(flowOpsBuilder::add);
191 break;
192 case REMOVE:
193 rules.stream()
Sho SHIMIZU45906042016-01-13 23:05:54 -0800194 .filter(Objects::nonNull)
Saurav Das822c4e22015-10-23 10:51:11 -0700195 .forEach(flowOpsBuilder::remove);
196 break;
197 default:
198 fail(fwd, ObjectiveError.UNKNOWN);
199 log.warn("Unknown forwarding type {}", fwd.op());
200 }
201
Saurav Das822c4e22015-10-23 10:51:11 -0700202 flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
203 @Override
204 public void onSuccess(FlowRuleOperations ops) {
205 pass(fwd);
206 }
207
208 @Override
209 public void onError(FlowRuleOperations ops) {
210 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
211 }
212 }));
Saurav Das822c4e22015-10-23 10:51:11 -0700213 }
214
215 @Override
216 public void next(NextObjective nextObjective) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800217 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
218 switch (nextObjective.op()) {
219 case ADD:
Saurav Das4f980082015-11-05 13:39:15 -0800220 if (nextGroup != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800221 log.warn("Cannot add next {} that already exists in device {}",
222 nextObjective.id(), deviceId);
223 return;
224 }
225 log.debug("Processing NextObjective id{} in dev{} - add group",
226 nextObjective.id(), deviceId);
Charles Chan425854b2016-04-11 15:32:12 -0700227 groupHandler.addGroup(nextObjective);
Saurav Das8a0732e2015-11-20 15:27:53 -0800228 break;
229 case ADD_TO_EXISTING:
230 if (nextGroup != null) {
231 log.debug("Processing NextObjective id{} in dev{} - add bucket",
232 nextObjective.id(), deviceId);
Charles Chan425854b2016-04-11 15:32:12 -0700233 groupHandler.addBucketToGroup(nextObjective, nextGroup);
Saurav Das4f980082015-11-05 13:39:15 -0800234 } else {
Saurav Das8a0732e2015-11-20 15:27:53 -0800235 // it is possible that group-chain has not been fully created yet
Saurav Das423fe2b2015-12-04 10:52:59 -0800236 log.debug("Waiting to add bucket to group for next-id:{} in dev:{}",
237 nextObjective.id(), deviceId);
238 // by design only one pending bucket is allowed for the group
Charles Chan425854b2016-04-11 15:32:12 -0700239 groupHandler.pendingBuckets.put(nextObjective.id(), nextObjective);
Saurav Das4f980082015-11-05 13:39:15 -0800240 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800241 break;
242 case REMOVE:
243 if (nextGroup == null) {
244 log.warn("Cannot remove next {} that does not exist in device {}",
245 nextObjective.id(), deviceId);
246 return;
247 }
248 log.debug("Processing NextObjective id{} in dev{} - remove group",
249 nextObjective.id(), deviceId);
Charles Chan425854b2016-04-11 15:32:12 -0700250 groupHandler.removeGroup(nextObjective, nextGroup);
Saurav Das8a0732e2015-11-20 15:27:53 -0800251 break;
252 case REMOVE_FROM_EXISTING:
253 if (nextGroup == null) {
254 log.warn("Cannot remove from next {} that does not exist in device {}",
255 nextObjective.id(), deviceId);
256 return;
257 }
258 log.debug("Processing NextObjective id{} in dev{} - remove bucket",
259 nextObjective.id(), deviceId);
Charles Chan425854b2016-04-11 15:32:12 -0700260 groupHandler.removeBucketFromGroup(nextObjective, nextGroup);
Saurav Das8a0732e2015-11-20 15:27:53 -0800261 break;
262 default:
Saurav Das4f980082015-11-05 13:39:15 -0800263 log.warn("Unsupported operation {}", nextObjective.op());
Saurav Das822c4e22015-10-23 10:51:11 -0700264 }
265 }
266
267 //////////////////////////////////////
268 // Flow handling
269 //////////////////////////////////////
270
271 /**
272 * As per OFDPA 2.0 TTP, filtering of VLAN ids, MAC addresses (for routing)
273 * and IP addresses configured on switch ports happen in different tables.
274 * Note that IP filtering rules need to be added to the ACL table, as there
275 * is no mechanism to send to controller via IP table.
276 *
277 * @param filt the filtering objective
278 * @param install indicates whether to add or remove the objective
279 * @param applicationId the application that sent this objective
280 */
Saurav Das52025962016-01-28 22:30:01 -0800281 protected void processFilter(FilteringObjective filt,
282 boolean install, ApplicationId applicationId) {
Saurav Das822c4e22015-10-23 10:51:11 -0700283 // This driver only processes filtering criteria defined with switch
284 // ports as the key
285 PortCriterion portCriterion = null;
286 EthCriterion ethCriterion = null;
287 VlanIdCriterion vidCriterion = null;
288 Collection<IPCriterion> ips = new ArrayList<IPCriterion>();
289 if (!filt.key().equals(Criteria.dummy()) &&
290 filt.key().type() == Criterion.Type.IN_PORT) {
291 portCriterion = (PortCriterion) filt.key();
292 } else {
293 log.warn("No key defined in filtering objective from app: {}. Not"
294 + "processing filtering objective", applicationId);
295 fail(filt, ObjectiveError.UNKNOWN);
296 return;
297 }
298 // convert filtering conditions for switch-intfs into flowrules
299 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
300 for (Criterion criterion : filt.conditions()) {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700301 if (criterion.type() == Criterion.Type.ETH_DST ||
302 criterion.type() == Criterion.Type.ETH_DST_MASKED) {
Saurav Das822c4e22015-10-23 10:51:11 -0700303 ethCriterion = (EthCriterion) criterion;
304 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
305 vidCriterion = (VlanIdCriterion) criterion;
306 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
307 ips.add((IPCriterion) criterion);
308 } else {
309 log.error("Unsupported filter {}", criterion);
310 fail(filt, ObjectiveError.UNSUPPORTED);
311 return;
312 }
313 }
314
Saurav Das0e99e2b2015-10-28 12:39:42 -0700315 VlanId assignedVlan = null;
Charles Chane849c192016-01-11 18:28:54 -0800316 // For VLAN cross-connect packets, use the configured VLAN
317 if (vidCriterion != null) {
318 if (vidCriterion.vlanId() != VlanId.NONE) {
319 assignedVlan = vidCriterion.vlanId();
320
321 // For untagged packets, assign a VLAN ID
322 } else {
323 if (filt.meta() == null) {
324 log.error("Missing metadata in filtering objective required " +
325 "for vlan assignment in dev {}", deviceId);
326 fail(filt, ObjectiveError.BADPARAMS);
327 return;
Saurav Das0e99e2b2015-10-28 12:39:42 -0700328 }
Charles Chane849c192016-01-11 18:28:54 -0800329 for (Instruction i : filt.meta().allInstructions()) {
330 if (i instanceof ModVlanIdInstruction) {
331 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
332 }
333 }
334 if (assignedVlan == null) {
335 log.error("Driver requires an assigned vlan-id to tag incoming "
336 + "untagged packets. Not processing vlan filters on "
337 + "device {}", deviceId);
338 fail(filt, ObjectiveError.BADPARAMS);
339 return;
340 }
Saurav Das0e99e2b2015-10-28 12:39:42 -0700341 }
342 }
343
Charles Chane849c192016-01-11 18:28:54 -0800344 if (ethCriterion == null || ethCriterion.mac().equals(MacAddress.NONE)) {
Saurav Das822c4e22015-10-23 10:51:11 -0700345 log.debug("filtering objective missing dstMac, cannot program TMAC table");
346 } else {
347 for (FlowRule tmacRule : processEthDstFilter(portCriterion, ethCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700348 vidCriterion, assignedVlan,
349 applicationId)) {
Saurav Das822c4e22015-10-23 10:51:11 -0700350 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
351 tmacRule, deviceId);
352 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
353 }
354 }
355
356 if (ethCriterion == null || vidCriterion == null) {
Charles Chane849c192016-01-11 18:28:54 -0800357 log.debug("filtering objective missing dstMac or VLAN, "
358 + "cannot program VLAN Table");
Saurav Das822c4e22015-10-23 10:51:11 -0700359 } else {
Charles Chan14967c22015-12-07 11:11:50 -0800360 /*
361 * NOTE: Separate vlan filtering rules and assignment rules
362 * into different stage in order to guarantee that filtering rules
Saurav Das52025962016-01-28 22:30:01 -0800363 * always go first, as required by ofdpa.
Charles Chan14967c22015-12-07 11:11:50 -0800364 */
365 List<FlowRule> allRules = processVlanIdFilter(
366 portCriterion, vidCriterion, assignedVlan, applicationId);
367 List<FlowRule> filteringRules = new ArrayList<>();
368 List<FlowRule> assignmentRules = new ArrayList<>();
369
370 allRules.forEach(flowRule -> {
371 ExtensionCriterion extCriterion =
372 (ExtensionCriterion) flowRule.selector().getCriterion(Criterion.Type.EXTENSION);
373 VlanId vlanId = ((OfdpaMatchVlanVid) extCriterion.extensionSelector()).vlanId();
Charles Chanbe8aea42016-02-24 12:04:47 -0800374 if (!vlanId.equals(VlanId.NONE)) {
Charles Chan14967c22015-12-07 11:11:50 -0800375 filteringRules.add(flowRule);
376 } else {
377 assignmentRules.add(flowRule);
378 }
379 });
380
381 for (FlowRule filteringRule : filteringRules) {
Saurav Das822c4e22015-10-23 10:51:11 -0700382 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
Charles Chan14967c22015-12-07 11:11:50 -0800383 filteringRule, deviceId);
384 ops = install ? ops.add(filteringRule) : ops.remove(filteringRule);
385 }
386
387 ops.newStage();
388
389 for (FlowRule assignmentRule : assignmentRules) {
390 log.debug("adding VLAN assignment rule in VLAN table: {} for dev: {}",
391 assignmentRule, deviceId);
392 ops = install ? ops.add(assignmentRule) : ops.remove(assignmentRule);
Saurav Das822c4e22015-10-23 10:51:11 -0700393 }
394 }
395
396 for (IPCriterion ipaddr : ips) {
397 // since we ignore port information for IP rules, and the same (gateway) IP
398 // can be configured on multiple ports, we make sure that we send
399 // only a single rule to the switch.
400 if (!sentIpFilters.contains(ipaddr)) {
401 sentIpFilters.add(ipaddr);
402 log.debug("adding IP filtering rules in ACL table {} for dev: {}",
403 ipaddr, deviceId);
404 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
405 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
406 selector.matchEthType(Ethernet.TYPE_IPV4);
407 selector.matchIPDst(ipaddr.ip());
408 treatment.setOutput(PortNumber.CONTROLLER);
409 FlowRule rule = DefaultFlowRule.builder()
410 .forDevice(deviceId)
411 .withSelector(selector.build())
412 .withTreatment(treatment.build())
413 .withPriority(HIGHEST_PRIORITY)
414 .fromApp(applicationId)
415 .makePermanent()
416 .forTable(ACL_TABLE).build();
417 ops = install ? ops.add(rule) : ops.remove(rule);
418 }
419 }
420
421 // apply filtering flow rules
422 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
423 @Override
424 public void onSuccess(FlowRuleOperations ops) {
425 log.info("Applied {} filtering rules in device {}",
426 ops.stages().get(0).size(), deviceId);
427 pass(filt);
428 }
429
430 @Override
431 public void onError(FlowRuleOperations ops) {
432 log.info("Failed to apply all filtering rules in dev {}", deviceId);
433 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
434 }
435 }));
436
437 }
438
439 /**
440 * Allows untagged packets into pipeline by assigning a vlan id.
Saurav Das0e99e2b2015-10-28 12:39:42 -0700441 * Vlan assignment is done by the application.
Saurav Das822c4e22015-10-23 10:51:11 -0700442 * Allows tagged packets into pipeline as per configured port-vlan info.
Saurav Das0e99e2b2015-10-28 12:39:42 -0700443 *
Saurav Das822c4e22015-10-23 10:51:11 -0700444 * @param portCriterion port on device for which this filter is programmed
445 * @param vidCriterion vlan assigned to port, or NONE for untagged
Saurav Das0e99e2b2015-10-28 12:39:42 -0700446 * @param assignedVlan assigned vlan-id for untagged packets
Saurav Das822c4e22015-10-23 10:51:11 -0700447 * @param applicationId for application programming this filter
448 * @return list of FlowRule for port-vlan filters
449 */
450 protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
451 VlanIdCriterion vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700452 VlanId assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700453 ApplicationId applicationId) {
Charles Chan14967c22015-12-07 11:11:50 -0800454 List<FlowRule> rules = new ArrayList<>();
Saurav Das822c4e22015-10-23 10:51:11 -0700455 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
456 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
Charles Chan14967c22015-12-07 11:11:50 -0800457 TrafficSelector.Builder preSelector = null;
458 TrafficTreatment.Builder preTreatment = null;
459
460
Saurav Das4f980082015-11-05 13:39:15 -0800461 treatment.transition(TMAC_TABLE);
462
463 VlanId storeVlan = null;
Saurav Das822c4e22015-10-23 10:51:11 -0700464 if (vidCriterion.vlanId() == VlanId.NONE) {
465 // untagged packets are assigned vlans
Charles Chanbe8aea42016-02-24 12:04:47 -0800466 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(VlanId.NONE);
Charles Chan14967c22015-12-07 11:11:50 -0800467 selector.extension(ofdpaMatchVlanVid, deviceId);
468 OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan);
469 treatment.extension(ofdpaSetVlanVid, deviceId);
Saurav Das52025962016-01-28 22:30:01 -0800470 // ofdpa requires an additional vlan match rule for the assigned vlan
471 // and it does not require the push when setting the assigned vlan.
472 // It also requires the extra rule to be sent to the switch before we
473 // send the untagged match rule.
474 // None of this in compliance with OF standard.
Saurav Das4f980082015-11-05 13:39:15 -0800475 storeVlan = assignedVlan;
Charles Chan14967c22015-12-07 11:11:50 -0800476
477 preSelector = DefaultTrafficSelector.builder();
478 OfdpaMatchVlanVid preOfdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan);
479 preSelector.extension(preOfdpaMatchVlanVid, deviceId);
480 preTreatment = DefaultTrafficTreatment.builder().transition(TMAC_TABLE);
481
Saurav Das4f980082015-11-05 13:39:15 -0800482 } else {
Charles Chan14967c22015-12-07 11:11:50 -0800483 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vidCriterion.vlanId());
484 selector.extension(ofdpaMatchVlanVid, deviceId);
Saurav Das4f980082015-11-05 13:39:15 -0800485 storeVlan = vidCriterion.vlanId();
Saurav Das822c4e22015-10-23 10:51:11 -0700486 }
Saurav Das822c4e22015-10-23 10:51:11 -0700487
488 // ofdpa cannot match on ALL portnumber, so we need to use separate
489 // rules for each port.
Charles Chan14967c22015-12-07 11:11:50 -0800490 List<PortNumber> portnums = new ArrayList<>();
Saurav Das822c4e22015-10-23 10:51:11 -0700491 if (portCriterion.port() == PortNumber.ALL) {
492 for (Port port : deviceService.getPorts(deviceId)) {
493 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
494 portnums.add(port.number());
495 }
496 }
497 } else {
498 portnums.add(portCriterion.port());
499 }
Saurav Das4f980082015-11-05 13:39:15 -0800500
Saurav Das822c4e22015-10-23 10:51:11 -0700501 for (PortNumber pnum : portnums) {
Saurav Das4f980082015-11-05 13:39:15 -0800502 // update storage
Charles Chan425854b2016-04-11 15:32:12 -0700503 groupHandler.port2Vlan.put(pnum, storeVlan);
504 Set<PortNumber> vlanPorts = groupHandler.vlan2Port.get(storeVlan);
Saurav Das4f980082015-11-05 13:39:15 -0800505 if (vlanPorts == null) {
506 vlanPorts = Collections.newSetFromMap(
507 new ConcurrentHashMap<PortNumber, Boolean>());
508 vlanPorts.add(pnum);
Charles Chan425854b2016-04-11 15:32:12 -0700509 groupHandler.vlan2Port.put(storeVlan, vlanPorts);
Saurav Das4f980082015-11-05 13:39:15 -0800510 } else {
511 vlanPorts.add(pnum);
512 }
513 // create rest of flowrule
Saurav Das822c4e22015-10-23 10:51:11 -0700514 selector.matchInPort(pnum);
515 FlowRule rule = DefaultFlowRule.builder()
516 .forDevice(deviceId)
517 .withSelector(selector.build())
518 .withTreatment(treatment.build())
519 .withPriority(DEFAULT_PRIORITY)
520 .fromApp(applicationId)
521 .makePermanent()
522 .forTable(VLAN_TABLE).build();
Charles Chan14967c22015-12-07 11:11:50 -0800523
524 if (preSelector != null) {
525 preSelector.matchInPort(pnum);
526 FlowRule preRule = DefaultFlowRule.builder()
527 .forDevice(deviceId)
528 .withSelector(preSelector.build())
529 .withTreatment(preTreatment.build())
530 .withPriority(DEFAULT_PRIORITY)
531 .fromApp(applicationId)
532 .makePermanent()
533 .forTable(VLAN_TABLE).build();
534 rules.add(preRule);
535 }
536
Saurav Das822c4e22015-10-23 10:51:11 -0700537 rules.add(rule);
538 }
539 return rules;
540 }
541
542 /**
543 * Allows routed packets with correct destination MAC to be directed
544 * to unicast-IP routing table or MPLS forwarding table.
Saurav Das822c4e22015-10-23 10:51:11 -0700545 *
546 * @param portCriterion port on device for which this filter is programmed
547 * @param ethCriterion dstMac of device for which is filter is programmed
548 * @param vidCriterion vlan assigned to port, or NONE for untagged
Saurav Das0e99e2b2015-10-28 12:39:42 -0700549 * @param assignedVlan assigned vlan-id for untagged packets
Saurav Das822c4e22015-10-23 10:51:11 -0700550 * @param applicationId for application programming this filter
551 * @return list of FlowRule for port-vlan filters
552
553 */
554 protected List<FlowRule> processEthDstFilter(PortCriterion portCriterion,
555 EthCriterion ethCriterion,
556 VlanIdCriterion vidCriterion,
Saurav Das0e99e2b2015-10-28 12:39:42 -0700557 VlanId assignedVlan,
Saurav Das822c4e22015-10-23 10:51:11 -0700558 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800559 // Consider PortNumber.ANY as wildcard. Match ETH_DST only
560 if (portCriterion != null && portCriterion.port() == PortNumber.ANY) {
561 return processEthDstOnlyFilter(ethCriterion, applicationId);
562 }
563
Charles Chan5b9df8d2016-03-28 22:21:40 -0700564 // Multicast MAC
565 if (ethCriterion.mask() != null) {
566 return processMcastEthDstFilter(ethCriterion, applicationId);
567 }
568
Saurav Das822c4e22015-10-23 10:51:11 -0700569 //handling untagged packets via assigned VLAN
570 if (vidCriterion.vlanId() == VlanId.NONE) {
Saurav Das0e99e2b2015-10-28 12:39:42 -0700571 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
Saurav Das822c4e22015-10-23 10:51:11 -0700572 }
573 // ofdpa cannot match on ALL portnumber, so we need to use separate
574 // rules for each port.
575 List<PortNumber> portnums = new ArrayList<PortNumber>();
576 if (portCriterion.port() == PortNumber.ALL) {
577 for (Port port : deviceService.getPorts(deviceId)) {
578 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
579 portnums.add(port.number());
580 }
581 }
582 } else {
583 portnums.add(portCriterion.port());
584 }
585
586 List<FlowRule> rules = new ArrayList<FlowRule>();
587 for (PortNumber pnum : portnums) {
Charles Chan14967c22015-12-07 11:11:50 -0800588 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vidCriterion.vlanId());
Saurav Das822c4e22015-10-23 10:51:11 -0700589 // for unicast IP packets
590 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
591 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
592 selector.matchInPort(pnum);
Charles Chan14967c22015-12-07 11:11:50 -0800593 selector.extension(ofdpaMatchVlanVid, deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700594 selector.matchEthType(Ethernet.TYPE_IPV4);
595 selector.matchEthDst(ethCriterion.mac());
596 treatment.transition(UNICAST_ROUTING_TABLE);
597 FlowRule rule = DefaultFlowRule.builder()
598 .forDevice(deviceId)
599 .withSelector(selector.build())
600 .withTreatment(treatment.build())
601 .withPriority(DEFAULT_PRIORITY)
602 .fromApp(applicationId)
603 .makePermanent()
604 .forTable(TMAC_TABLE).build();
605 rules.add(rule);
606 //for MPLS packets
607 selector = DefaultTrafficSelector.builder();
608 treatment = DefaultTrafficTreatment.builder();
609 selector.matchInPort(pnum);
Charles Chan14967c22015-12-07 11:11:50 -0800610 selector.extension(ofdpaMatchVlanVid, deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700611 selector.matchEthType(Ethernet.MPLS_UNICAST);
612 selector.matchEthDst(ethCriterion.mac());
613 treatment.transition(MPLS_TABLE_0);
614 rule = DefaultFlowRule.builder()
615 .forDevice(deviceId)
616 .withSelector(selector.build())
617 .withTreatment(treatment.build())
618 .withPriority(DEFAULT_PRIORITY)
619 .fromApp(applicationId)
620 .makePermanent()
621 .forTable(TMAC_TABLE).build();
622 rules.add(rule);
623 }
624 return rules;
625 }
626
Charles Chan5270ed02016-01-30 23:22:37 -0800627 protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion,
628 ApplicationId applicationId) {
629 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
630 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
631 selector.matchEthType(Ethernet.TYPE_IPV4);
632 selector.matchEthDst(ethCriterion.mac());
633 treatment.transition(UNICAST_ROUTING_TABLE);
634 FlowRule rule = DefaultFlowRule.builder()
635 .forDevice(deviceId)
636 .withSelector(selector.build())
637 .withTreatment(treatment.build())
638 .withPriority(DEFAULT_PRIORITY)
639 .fromApp(applicationId)
640 .makePermanent()
641 .forTable(TMAC_TABLE).build();
642 return ImmutableList.<FlowRule>builder().add(rule).build();
643 }
644
Charles Chan5b9df8d2016-03-28 22:21:40 -0700645 protected List<FlowRule> processMcastEthDstFilter(EthCriterion ethCriterion,
646 ApplicationId applicationId) {
647 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
648 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
649 selector.matchEthType(Ethernet.TYPE_IPV4);
650 selector.matchEthDstMasked(ethCriterion.mac(), ethCriterion.mask());
651 treatment.transition(MULTICAST_ROUTING_TABLE);
652 FlowRule rule = DefaultFlowRule.builder()
653 .forDevice(deviceId)
654 .withSelector(selector.build())
655 .withTreatment(treatment.build())
656 .withPriority(DEFAULT_PRIORITY)
657 .fromApp(applicationId)
658 .makePermanent()
659 .forTable(TMAC_TABLE).build();
660 return ImmutableList.<FlowRule>builder().add(rule).build();
661 }
662
Saurav Das822c4e22015-10-23 10:51:11 -0700663 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
664 switch (fwd.flag()) {
665 case SPECIFIC:
666 return processSpecific(fwd);
667 case VERSATILE:
668 return processVersatile(fwd);
669 default:
670 fail(fwd, ObjectiveError.UNKNOWN);
671 log.warn("Unknown forwarding flag {}", fwd.flag());
672 }
673 return Collections.emptySet();
674 }
675
676 /**
677 * In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the
678 * ACL table.
679 * @param fwd the forwarding objective of type 'versatile'
680 * @return a collection of flow rules to be sent to the switch. An empty
681 * collection may be returned if there is a problem in processing
682 * the flow rule
683 */
Saurav Das52025962016-01-28 22:30:01 -0800684 protected Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
Saurav Das822c4e22015-10-23 10:51:11 -0700685 log.info("Processing versatile forwarding objective");
Saurav Das822c4e22015-10-23 10:51:11 -0700686
687 EthTypeCriterion ethType =
Saurav Das77b5e902016-01-27 17:01:59 -0800688 (EthTypeCriterion) fwd.selector().getCriterion(Criterion.Type.ETH_TYPE);
Saurav Das822c4e22015-10-23 10:51:11 -0700689 if (ethType == null) {
690 log.error("Versatile forwarding objective must include ethType");
691 fail(fwd, ObjectiveError.BADPARAMS);
692 return Collections.emptySet();
693 }
694 if (fwd.nextId() == null && fwd.treatment() == null) {
695 log.error("Forwarding objective {} from {} must contain "
696 + "nextId or Treatment", fwd.selector(), fwd.appId());
Zsolt Haraszti9faab752016-02-17 15:55:20 -0800697 fail(fwd, ObjectiveError.BADPARAMS);
Saurav Das822c4e22015-10-23 10:51:11 -0700698 return Collections.emptySet();
699 }
Saurav Das49cb5a12016-01-16 22:54:07 -0800700
Saurav Das77b5e902016-01-27 17:01:59 -0800701 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
702 fwd.selector().criteria().forEach(criterion -> {
703 if (criterion instanceof VlanIdCriterion) {
704 VlanId vlanId = ((VlanIdCriterion) criterion).vlanId();
705 // ensure that match does not include vlan = NONE as OF-DPA does not
706 // match untagged packets this way in the ACL table.
707 if (vlanId.equals(VlanId.NONE)) {
708 return;
709 }
710 OfdpaMatchVlanVid ofdpaMatchVlanVid =
711 new OfdpaMatchVlanVid(vlanId);
712 sbuilder.extension(ofdpaMatchVlanVid, deviceId);
713 } else {
714 sbuilder.add(criterion);
715 }
716 });
717
Saurav Das822c4e22015-10-23 10:51:11 -0700718 // XXX driver does not currently do type checking as per Tables 65-67 in
719 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
Saurav Das49cb5a12016-01-16 22:54:07 -0800720 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
721 if (fwd.treatment() != null) {
722 for (Instruction ins : fwd.treatment().allInstructions()) {
723 if (ins instanceof OutputInstruction) {
724 OutputInstruction o = (OutputInstruction) ins;
725 if (o.port() == PortNumber.CONTROLLER) {
726 ttBuilder.add(o);
727 } else {
728 log.warn("Only allowed treatments in versatile forwarding "
729 + "objectives are punts to the controller");
730 }
731 } else {
732 log.warn("Cannot process instruction in versatile fwd {}", ins);
733 }
Saurav Das822c4e22015-10-23 10:51:11 -0700734 }
735 }
Saurav Das822c4e22015-10-23 10:51:11 -0700736 if (fwd.nextId() != null) {
Saurav Das49cb5a12016-01-16 22:54:07 -0800737 // overide case
738 NextGroup next = getGroupForNextObjective(fwd.nextId());
739 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
740 // we only need the top level group's key to point the flow to it
741 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
742 if (group == null) {
743 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
744 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
745 fail(fwd, ObjectiveError.GROUPMISSING);
746 return Collections.emptySet();
747 }
748 ttBuilder.deferred().group(group.id());
Saurav Das822c4e22015-10-23 10:51:11 -0700749 }
Saurav Das49cb5a12016-01-16 22:54:07 -0800750
751 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
752 .fromApp(fwd.appId())
753 .withPriority(fwd.priority())
754 .forDevice(deviceId)
Saurav Das77b5e902016-01-27 17:01:59 -0800755 .withSelector(sbuilder.build())
Saurav Das49cb5a12016-01-16 22:54:07 -0800756 .withTreatment(ttBuilder.build())
757 .makePermanent()
758 .forTable(ACL_TABLE);
759 return Collections.singletonList(ruleBuilder.build());
Saurav Das822c4e22015-10-23 10:51:11 -0700760 }
761
762 /**
763 * In the OF-DPA 2.0 pipeline, specific forwarding refers to the IP table
Saurav Das8a0732e2015-11-20 15:27:53 -0800764 * (unicast or multicast) or the L2 table (mac + vlan) or the MPLS table.
Saurav Das822c4e22015-10-23 10:51:11 -0700765 *
766 * @param fwd the forwarding objective of type 'specific'
767 * @return a collection of flow rules. Typically there will be only one
768 * for this type of forwarding objective. An empty set may be
769 * returned if there is an issue in processing the objective.
770 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800771 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
Saurav Das4ce45962015-11-24 23:21:05 -0800772 log.trace("Processing specific fwd objective:{} in dev:{} with next:{}",
773 fwd.id(), deviceId, fwd.nextId());
774 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
775 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
776
777 if (isEthTypeObj) {
778 return processEthTypeSpecific(fwd);
779 } else if (isEthDstObj) {
780 return processEthDstSpecific(fwd);
781 } else {
782 log.warn("processSpecific: Unsupported forwarding objective "
783 + "criteria fwd:{} in dev:{}", fwd.nextId(), deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700784 fail(fwd, ObjectiveError.UNSUPPORTED);
785 return Collections.emptySet();
786 }
Saurav Das4ce45962015-11-24 23:21:05 -0800787 }
788
789 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
790 TrafficSelector selector = fwd.selector();
791 EthTypeCriterion ethType = (EthTypeCriterion) selector
792 .getCriterion(Criterion.Type.ETH_TYPE);
Charles Chan188ebf52015-12-23 00:15:11 -0800793 return !((ethType == null) ||
Saurav Das4ce45962015-11-24 23:21:05 -0800794 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
Charles Chan188ebf52015-12-23 00:15:11 -0800795 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST)));
Saurav Das4ce45962015-11-24 23:21:05 -0800796 }
797
798 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
799 TrafficSelector selector = fwd.selector();
800 EthCriterion ethDst = (EthCriterion) selector
801 .getCriterion(Criterion.Type.ETH_DST);
802 VlanIdCriterion vlanId = (VlanIdCriterion) selector
803 .getCriterion(Criterion.Type.VLAN_VID);
Charles Chan188ebf52015-12-23 00:15:11 -0800804 return !(ethDst == null && vlanId == null);
Saurav Das4ce45962015-11-24 23:21:05 -0800805 }
806
807 /**
808 * Handles forwarding rules to the IP and MPLS tables.
809 *
810 * @param fwd the forwarding objective
811 * @return A collection of flow rules, or an empty set
812 */
813 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
814 TrafficSelector selector = fwd.selector();
815 EthTypeCriterion ethType =
816 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
Flavio Castroe10fa242016-01-15 12:43:51 -0800817 boolean defaultRule = false;
818 boolean popMpls = false;
Charles Chan188ebf52015-12-23 00:15:11 -0800819 int forTableId;
Saurav Das8a0732e2015-11-20 15:27:53 -0800820 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
Charles Chan14967c22015-12-07 11:11:50 -0800821 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
Flavio Castroe10fa242016-01-15 12:43:51 -0800822 TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder();
Charles Chan14967c22015-12-07 11:11:50 -0800823
Flavio Castroe10fa242016-01-15 12:43:51 -0800824 /*
825 * NOTE: The switch does not support matching 0.0.0.0/0.
826 * Split it into 0.0.0.0/1 and 128.0.0.0/1
827 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800828 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Flavio Castroe10fa242016-01-15 12:43:51 -0800829 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700830 if (ipv4Dst.isMulticast()) {
831 if (ipv4Dst.prefixLength() != 32) {
832 log.warn("Multicast specific forwarding objective can only be /32");
833 fail(fwd, ObjectiveError.BADPARAMS);
834 return ImmutableSet.of();
835 }
836 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
837 if (assignedVlan == null) {
838 log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
839 fail(fwd, ObjectiveError.BADPARAMS);
840 return ImmutableSet.of();
841 }
842 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan);
843 filteredSelector.extension(ofdpaMatchVlanVid, deviceId);
844 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
845 forTableId = MULTICAST_ROUTING_TABLE;
846 log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
847 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800848 } else {
Charles Chan5b9df8d2016-03-28 22:21:40 -0700849 if (ipv4Dst.prefixLength() > 0) {
850 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
851 } else {
852 filteredSelector.matchEthType(Ethernet.TYPE_IPV4)
853 .matchIPDst(IpPrefix.valueOf("0.0.0.0/1"));
854 complementarySelector.matchEthType(Ethernet.TYPE_IPV4)
855 .matchIPDst(IpPrefix.valueOf("128.0.0.0/1"));
856 defaultRule = true;
857 }
858 forTableId = UNICAST_ROUTING_TABLE;
859 log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
860 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800861 }
Charles Chan14967c22015-12-07 11:11:50 -0800862
863 if (fwd.treatment() != null) {
864 for (Instruction instr : fwd.treatment().allInstructions()) {
865 if (instr instanceof L3ModificationInstruction &&
866 ((L3ModificationInstruction) instr).subtype() == L3SubType.DEC_TTL) {
867 tb.deferred().add(instr);
868 }
869 }
870 }
871
Saurav Das8a0732e2015-11-20 15:27:53 -0800872 } else {
873 filteredSelector
874 .matchEthType(Ethernet.MPLS_UNICAST)
875 .matchMplsLabel(((MplsCriterion)
876 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
877 MplsBosCriterion bos = (MplsBosCriterion) selector
878 .getCriterion(Criterion.Type.MPLS_BOS);
879 if (bos != null) {
880 filteredSelector.matchMplsBos(bos.mplsBos());
881 }
882 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800883 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
884 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das822c4e22015-10-23 10:51:11 -0700885
Charles Chan14967c22015-12-07 11:11:50 -0800886 if (fwd.treatment() != null) {
887 for (Instruction instr : fwd.treatment().allInstructions()) {
888 if (instr instanceof L2ModificationInstruction &&
889 ((L2ModificationInstruction) instr).subtype() == L2SubType.MPLS_POP) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800890 popMpls = true;
Charles Chan14967c22015-12-07 11:11:50 -0800891 tb.immediate().add(instr);
892 }
893 if (instr instanceof L3ModificationInstruction &&
894 ((L3ModificationInstruction) instr).subtype() == L3SubType.DEC_TTL) {
895 // FIXME Should modify the app to send the correct DEC_MPLS_TTL instruction
896 tb.immediate().decMplsTtl();
897 }
898 if (instr instanceof L3ModificationInstruction &&
899 ((L3ModificationInstruction) instr).subtype() == L3SubType.TTL_IN) {
900 tb.immediate().add(instr);
901 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800902 }
903 }
904 }
Saurav Das822c4e22015-10-23 10:51:11 -0700905
906 if (fwd.nextId() != null) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800907 if (forTableId == MPLS_TABLE_1 && !popMpls) {
908 log.warn("SR CONTINUE case cannot be handled as MPLS ECMP "
909 + "is not implemented in OF-DPA yet. Aborting this flow "
910 + "in this device {}", deviceId);
911 // XXX We could convert to forwarding to a single-port, via a
912 // MPLS interface, or a MPLS SWAP (with-same) but that would
913 // have to be handled in the next-objective. Also the pop-mpls
914 // logic used here won't work in non-BoS case.
Saurav Das4ce45962015-11-24 23:21:05 -0800915 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Saurav Das8a0732e2015-11-20 15:27:53 -0800916 return Collections.emptySet();
917 }
918
Saurav Das423fe2b2015-12-04 10:52:59 -0800919 NextGroup next = getGroupForNextObjective(fwd.nextId());
920 if (next != null) {
921 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
922 // we only need the top level group's key to point the flow to it
923 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
924 if (group == null) {
925 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
926 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
927 fail(fwd, ObjectiveError.GROUPMISSING);
928 return Collections.emptySet();
929 }
930 tb.deferred().group(group.id());
Saurav Das822c4e22015-10-23 10:51:11 -0700931 }
Saurav Das822c4e22015-10-23 10:51:11 -0700932 }
933 tb.transition(ACL_TABLE);
934 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
935 .fromApp(fwd.appId())
936 .withPriority(fwd.priority())
937 .forDevice(deviceId)
Saurav Das8a0732e2015-11-20 15:27:53 -0800938 .withSelector(filteredSelector.build())
939 .withTreatment(tb.build())
940 .forTable(forTableId);
Saurav Das822c4e22015-10-23 10:51:11 -0700941
942 if (fwd.permanent()) {
943 ruleBuilder.makePermanent();
944 } else {
945 ruleBuilder.makeTemporary(fwd.timeout());
946 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800947 Collection<FlowRule> flowRuleCollection = new ArrayList<>();
948 flowRuleCollection.add(ruleBuilder.build());
949 if (defaultRule) {
950 FlowRule.Builder rule = DefaultFlowRule.builder()
951 .fromApp(fwd.appId())
952 .withPriority(fwd.priority())
953 .forDevice(deviceId)
954 .withSelector(complementarySelector.build())
955 .withTreatment(tb.build())
956 .forTable(forTableId);
957 if (fwd.permanent()) {
958 rule.makePermanent();
959 } else {
960 rule.makeTemporary(fwd.timeout());
961 }
962 flowRuleCollection.add(rule.build());
963 log.debug("Default rule 0.0.0.0/0 is being installed two rules");
964 }
Saurav Das822c4e22015-10-23 10:51:11 -0700965
Flavio Castroe10fa242016-01-15 12:43:51 -0800966 return flowRuleCollection;
Saurav Das822c4e22015-10-23 10:51:11 -0700967 }
968
Saurav Das4ce45962015-11-24 23:21:05 -0800969 /**
970 * Handles forwarding rules to the L2 bridging table. Flow actions are not
971 * allowed in the bridging table - instead we use L2 Interface group or
972 * L2 flood group
973 *
974 * @param fwd the forwarding objective
975 * @return A collection of flow rules, or an empty set
976 */
977 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
978 List<FlowRule> rules = new ArrayList<>();
979
980 // Build filtered selector
981 TrafficSelector selector = fwd.selector();
982 EthCriterion ethCriterion = (EthCriterion) selector
983 .getCriterion(Criterion.Type.ETH_DST);
984 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
985 .getCriterion(Criterion.Type.VLAN_VID);
986
987 if (vlanIdCriterion == null) {
988 log.warn("Forwarding objective for bridging requires vlan. Not "
989 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
990 fail(fwd, ObjectiveError.BADPARAMS);
991 return Collections.emptySet();
992 }
993
994 TrafficSelector.Builder filteredSelectorBuilder =
995 DefaultTrafficSelector.builder();
996 // Do not match MacAddress for subnet broadcast entry
997 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
998 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
999 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
1000 fwd.id(), fwd.nextId(), deviceId);
1001 } else {
1002 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
1003 + "in dev:{} for vlan:{}",
1004 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
1005 }
Charles Chan14967c22015-12-07 11:11:50 -08001006 OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vlanIdCriterion.vlanId());
1007 filteredSelectorBuilder.extension(ofdpaMatchVlanVid, deviceId);
Saurav Das4ce45962015-11-24 23:21:05 -08001008 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
1009
1010 if (fwd.treatment() != null) {
1011 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
1012 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
1013 }
1014
1015 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
1016 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -08001017 NextGroup next = getGroupForNextObjective(fwd.nextId());
Saurav Das4ce45962015-11-24 23:21:05 -08001018 if (next != null) {
1019 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1020 // we only need the top level group's key to point the flow to it
1021 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
1022 if (group != null) {
1023 treatmentBuilder.deferred().group(group.id());
1024 } else {
1025 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
1026 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
1027 fail(fwd, ObjectiveError.GROUPMISSING);
1028 return Collections.emptySet();
1029 }
1030 }
1031 }
1032 treatmentBuilder.immediate().transition(ACL_TABLE);
1033 TrafficTreatment filteredTreatment = treatmentBuilder.build();
1034
1035 // Build bridging table entries
1036 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
1037 flowRuleBuilder.fromApp(fwd.appId())
1038 .withPriority(fwd.priority())
1039 .forDevice(deviceId)
1040 .withSelector(filteredSelector)
1041 .withTreatment(filteredTreatment)
1042 .forTable(BRIDGING_TABLE);
1043 if (fwd.permanent()) {
1044 flowRuleBuilder.makePermanent();
1045 } else {
1046 flowRuleBuilder.makeTemporary(fwd.timeout());
1047 }
1048 rules.add(flowRuleBuilder.build());
1049 return rules;
1050 }
1051
Saurav Das423fe2b2015-12-04 10:52:59 -08001052 protected NextGroup getGroupForNextObjective(Integer nextId) {
1053 NextGroup next = flowObjectiveStore.getNextGroup(nextId);
1054 if (next != null) {
1055 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
1056 if (gkeys != null && !gkeys.isEmpty()) {
1057 return next;
1058 } else {
1059 log.warn("Empty next group found in FlowObjective store for "
1060 + "next-id:{} in dev:{}", nextId, deviceId);
1061 }
1062 } else {
1063 log.warn("next-id {} not found in Flow objective store for dev:{}",
1064 nextId, deviceId);
1065 }
1066 return null;
1067 }
1068
Charles Chan188ebf52015-12-23 00:15:11 -08001069 protected static void pass(Objective obj) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001070 obj.context().ifPresent(context -> context.onSuccess(obj));
Saurav Das822c4e22015-10-23 10:51:11 -07001071 }
1072
Charles Chan188ebf52015-12-23 00:15:11 -08001073 protected static void fail(Objective obj, ObjectiveError error) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -08001074 obj.context().ifPresent(context -> context.onError(obj, error));
Saurav Das822c4e22015-10-23 10:51:11 -07001075 }
Saurav Das24431192016-03-07 19:13:00 -08001076
Saurav Das24431192016-03-07 19:13:00 -08001077 @Override
1078 public List<String> getNextMappings(NextGroup nextGroup) {
1079 List<String> mappings = new ArrayList<>();
1080 List<Deque<GroupKey>> gkeys = appKryo.deserialize(nextGroup.data());
1081 for (Deque<GroupKey> gkd : gkeys) {
1082 Group lastGroup = null;
Saurav Das8be4e3a2016-03-11 17:19:07 -08001083 StringBuffer gchain = new StringBuffer();
Saurav Das24431192016-03-07 19:13:00 -08001084 for (GroupKey gk : gkd) {
1085 Group g = groupService.getGroup(deviceId, gk);
Saurav Das8be4e3a2016-03-11 17:19:07 -08001086 if (g == null) {
1087 gchain.append(" ERROR").append(" -->");
1088 continue;
1089 }
1090 gchain.append(" 0x").append(Integer.toHexString(g.id().id()))
1091 .append(" -->");
Saurav Das24431192016-03-07 19:13:00 -08001092 lastGroup = g;
1093 }
1094 // add port information for last group in group-chain
1095 for (Instruction i: lastGroup.buckets().buckets().get(0).treatment().allInstructions()) {
1096 if (i instanceof OutputInstruction) {
Saurav Das8be4e3a2016-03-11 17:19:07 -08001097 gchain.append(" port:").append(((OutputInstruction) i).port());
Saurav Das24431192016-03-07 19:13:00 -08001098 }
1099 }
Saurav Das8be4e3a2016-03-11 17:19:07 -08001100 mappings.add(gchain.toString());
Saurav Das24431192016-03-07 19:13:00 -08001101 }
1102 return mappings;
1103 }
Charles Chan5b9df8d2016-03-28 22:21:40 -07001104
1105 protected static VlanId readVlanFromSelector(TrafficSelector selector) {
1106 Criterion criterion = selector.getCriterion(Criterion.Type.VLAN_VID);
1107 return (criterion == null)
1108 ? null : ((VlanIdCriterion) criterion).vlanId();
1109 }
1110
1111 protected static IpPrefix readIpDstFromSelector(TrafficSelector selector) {
1112 Criterion criterion = selector.getCriterion(Criterion.Type.IPV4_DST);
1113 return (criterion == null) ? null : ((IPCriterion) criterion).ip();
1114 }
Saurav Das822c4e22015-10-23 10:51:11 -07001115}