blob: 377a0c73a7e7f4b2a20c44a1bdeb9a50cba9f6ca [file] [log] [blame]
Thomas Vachuska58de4162015-09-10 16:15:33 -07001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Thomas Vachuska58de4162015-09-10 16:15:33 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Yi Tsengef19de12017-04-24 11:33:05 -070016package org.onosproject.driver.pipeline.ofdpa;
Saurav Das558afec2015-05-31 17:12:48 -070017
Charles Chan5270ed02016-01-30 23:22:37 -080018import com.google.common.collect.ImmutableList;
Charles Chan5b9df8d2016-03-28 22:21:40 -070019import com.google.common.collect.ImmutableSet;
Saurav Das8a0732e2015-11-20 15:27:53 -080020import org.onlab.packet.Ethernet;
Flavio Castroe10fa242016-01-15 12:43:51 -080021import org.onlab.packet.IpPrefix;
Saurav Das2857f382015-11-03 14:39:27 -080022import org.onlab.packet.VlanId;
23import org.onosproject.core.ApplicationId;
Yi Tsengfa394de2017-02-01 11:26:40 -080024import org.onosproject.core.GroupId;
Saurav Das2857f382015-11-03 14:39:27 -080025import org.onosproject.net.Port;
26import org.onosproject.net.PortNumber;
Saurav Das8a0732e2015-11-20 15:27:53 -080027import org.onosproject.net.behaviour.NextGroup;
Charles Chan425854b2016-04-11 15:32:12 -070028import org.onosproject.net.behaviour.PipelinerContext;
Saurav Das558afec2015-05-31 17:12:48 -070029import org.onosproject.net.flow.DefaultFlowRule;
30import org.onosproject.net.flow.DefaultTrafficSelector;
31import org.onosproject.net.flow.DefaultTrafficTreatment;
32import org.onosproject.net.flow.FlowRule;
33import org.onosproject.net.flow.FlowRuleOperations;
34import org.onosproject.net.flow.FlowRuleOperationsContext;
35import org.onosproject.net.flow.TrafficSelector;
36import org.onosproject.net.flow.TrafficTreatment;
Saurav Das4ce45962015-11-24 23:21:05 -080037import org.onosproject.net.flow.criteria.Criteria;
Saurav Das8a0732e2015-11-20 15:27:53 -080038import org.onosproject.net.flow.criteria.Criterion;
Saurav Das4ce45962015-11-24 23:21:05 -080039import org.onosproject.net.flow.criteria.EthCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080040import org.onosproject.net.flow.criteria.EthTypeCriterion;
41import org.onosproject.net.flow.criteria.IPCriterion;
Pier Ventree0ae7a32016-11-23 09:57:42 -080042import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion;
43import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080044import org.onosproject.net.flow.criteria.MplsBosCriterion;
45import org.onosproject.net.flow.criteria.MplsCriterion;
Saurav Das2857f382015-11-03 14:39:27 -080046import org.onosproject.net.flow.criteria.PortCriterion;
47import org.onosproject.net.flow.criteria.VlanIdCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080048import org.onosproject.net.flow.instructions.Instruction;
Saurav Das52025962016-01-28 22:30:01 -080049import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Charles Chan40132b32017-01-22 00:19:37 -080050import org.onosproject.net.flow.instructions.L3ModificationInstruction;
Saurav Das8a0732e2015-11-20 15:27:53 -080051import org.onosproject.net.flowobjective.ForwardingObjective;
52import org.onosproject.net.flowobjective.ObjectiveError;
Charles Chan0f43e472017-02-14 14:00:16 -080053import org.onosproject.net.group.DefaultGroupBucket;
54import org.onosproject.net.group.DefaultGroupDescription;
55import org.onosproject.net.group.DefaultGroupKey;
Saurav Das8a0732e2015-11-20 15:27:53 -080056import org.onosproject.net.group.Group;
Charles Chan0f43e472017-02-14 14:00:16 -080057import org.onosproject.net.group.GroupBucket;
58import org.onosproject.net.group.GroupBuckets;
59import org.onosproject.net.group.GroupDescription;
Saurav Das8a0732e2015-11-20 15:27:53 -080060import org.onosproject.net.group.GroupKey;
Charles Chanf57a8252016-06-29 19:12:37 -070061import org.onosproject.net.packet.PacketPriority;
Saurav Das558afec2015-05-31 17:12:48 -070062import org.slf4j.Logger;
63
Jonathan Hart855179c2016-04-26 07:40:04 -070064import java.util.ArrayList;
65import java.util.Collection;
66import java.util.Collections;
67import java.util.Deque;
68import java.util.List;
Charles Chan0f43e472017-02-14 14:00:16 -080069import java.util.Objects;
Jonathan Hart855179c2016-04-26 07:40:04 -070070
Pier Ventree0ae7a32016-11-23 09:57:42 -080071import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
Charles Chand05f54b2017-02-13 11:56:54 -080072import static org.onlab.packet.MacAddress.BROADCAST;
73import static org.onlab.packet.MacAddress.NONE;
Yi Tsengef19de12017-04-24 11:33:05 -070074import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*;
Jonathan Hart855179c2016-04-26 07:40:04 -070075import static org.slf4j.LoggerFactory.getLogger;
76
Saurav Das558afec2015-05-31 17:12:48 -070077
78/**
Charles Chan40132b32017-01-22 00:19:37 -080079 * Driver for software switch emulation of the OFDPA pipeline.
Saurav Das52025962016-01-28 22:30:01 -080080 * The software switch is the CPqD OF 1.3 switch. Unfortunately the CPqD switch
81 * does not handle vlan tags and mpls labels simultaneously, which requires us
82 * to do some workarounds in the driver. This driver is meant for the use of
83 * the cpqd switch when MPLS is required. As a result this driver works only
84 * on incoming untagged packets.
Saurav Das558afec2015-05-31 17:12:48 -070085 */
Charles Chan361154b2016-03-24 10:23:39 -070086public class CpqdOfdpa2Pipeline extends Ofdpa2Pipeline {
Saurav Das558afec2015-05-31 17:12:48 -070087
88 private final Logger log = getLogger(getClass());
89
Charles Chan40132b32017-01-22 00:19:37 -080090 /**
Charles Chan0f43e472017-02-14 14:00:16 -080091 * Table that determines whether VLAN is popped before punting to controller.
92 * <p>
93 * This is a non-OFDPA table to emulate OFDPA packet in behavior.
94 * VLAN will be popped before punting if the VLAN is internally assigned.
95 * <p>
96 * Also note that 63 is the max table number in CpqD.
97 */
98 private static final int PUNT_TABLE = 63;
99
100 /**
101 * A static indirect group that pop vlan and punt to controller.
102 * <p>
103 * The purpose of using a group instead of immediate action is that this
104 * won't affect another copy on the data plane when write action exists.
105 */
106 private static final int POP_VLAN_PUNT_GROUP_ID = 0xc0000000;
107
Charles Chan053b1cb2017-03-22 16:56:35 -0700108 @Override
109 protected boolean requireVlanExtensions() {
110 return false;
111 }
112
Charles Chan0f43e472017-02-14 14:00:16 -0800113 /**
Charles Chan40132b32017-01-22 00:19:37 -0800114 * Determines whether this pipeline support copy ttl instructions or not.
115 *
116 * @return true if copy ttl instructions are supported
117 */
118 protected boolean supportCopyTtl() {
119 return true;
120 }
121
Charles Chan0f43e472017-02-14 14:00:16 -0800122 /**
123 * Determines whether this pipeline support push mpls to vlan-tagged packets or not.
124 * <p>
125 * If not support, pop vlan before push entering unicast and mpls table.
126 * Side effect: HostService learns redundant hosts with same MAC but
127 * different VLAN. No known side effect on the network reachability.
128 *
129 * @return true if push mpls to vlan-tagged packets is supported
130 */
131 protected boolean supportTaggedMpls() {
132 return false;
133 }
134
135 /**
136 * Determines whether this pipeline support punt action in group bucket.
137 *
138 * @return true if punt action in group bucket is supported
139 */
140 protected boolean supportPuntGroup() {
141 return false;
142 }
143
Charles Chan425854b2016-04-11 15:32:12 -0700144 @Override
Charles Chan40132b32017-01-22 00:19:37 -0800145 protected void initDriverId() {
Charles Chan425854b2016-04-11 15:32:12 -0700146 driverId = coreService.registerApplication(
147 "org.onosproject.driver.CpqdOfdpa2Pipeline");
Charles Chan40132b32017-01-22 00:19:37 -0800148 }
Charles Chan425854b2016-04-11 15:32:12 -0700149
Charles Chan40132b32017-01-22 00:19:37 -0800150 @Override
151 protected void initGroupHander(PipelinerContext context) {
152 groupHandler = new CpqdOfdpa2GroupHandler();
153 groupHandler.init(deviceId, context);
Charles Chan425854b2016-04-11 15:32:12 -0700154 }
155
Saurav Das4ce45962015-11-24 23:21:05 -0800156 /*
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700157 * Cpqd emulation does not require the non OF-standard rules for
158 * matching untagged packets that ofdpa uses.
Saurav Das4ce45962015-11-24 23:21:05 -0800159 *
160 * (non-Javadoc)
161 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter
162 */
Saurav Das558afec2015-05-31 17:12:48 -0700163 @Override
Saurav Das2857f382015-11-03 14:39:27 -0800164 protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
165 VlanIdCriterion vidCriterion,
166 VlanId assignedVlan,
167 ApplicationId applicationId) {
Charles Chan79769232016-07-05 16:34:39 -0700168 List<FlowRule> rules = new ArrayList<>();
Saurav Das2857f382015-11-03 14:39:27 -0800169 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
170 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
171 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800172 treatment.transition(TMAC_TABLE);
173
Saurav Das2857f382015-11-03 14:39:27 -0800174 if (vidCriterion.vlanId() == VlanId.NONE) {
175 // untagged packets are assigned vlans
176 treatment.pushVlan().setVlanId(assignedVlan);
177 }
Saurav Das2857f382015-11-03 14:39:27 -0800178
179 // ofdpa cannot match on ALL portnumber, so we need to use separate
180 // rules for each port.
181 List<PortNumber> portnums = new ArrayList<PortNumber>();
182 if (portCriterion.port() == PortNumber.ALL) {
183 for (Port port : deviceService.getPorts(deviceId)) {
184 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
185 portnums.add(port.number());
186 }
187 }
188 } else {
189 portnums.add(portCriterion.port());
190 }
Saurav Das4f980082015-11-05 13:39:15 -0800191
Saurav Das2857f382015-11-03 14:39:27 -0800192 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800193 // NOTE: Emulating OFDPA behavior by popping off internal assigned
194 // VLAN before sending to controller
195 if (supportPuntGroup() && vidCriterion.vlanId() == VlanId.NONE) {
Yi Tsengef19de12017-04-24 11:33:05 -0700196 GroupKey groupKey = popVlanPuntGroupKey();
Charles Chan0f43e472017-02-14 14:00:16 -0800197 Group group = groupService.getGroup(deviceId, groupKey);
198 if (group != null) {
199 rules.add(buildPuntTableRule(pnum, assignedVlan));
200 } else {
201 log.info("popVlanPuntGroup not found in dev:{}", deviceId);
202 return Collections.emptyList();
203 }
204 }
205
Saurav Das4f980082015-11-05 13:39:15 -0800206 // create rest of flowrule
Saurav Das2857f382015-11-03 14:39:27 -0800207 selector.matchInPort(pnum);
208 FlowRule rule = DefaultFlowRule.builder()
209 .forDevice(deviceId)
210 .withSelector(selector.build())
211 .withTreatment(treatment.build())
212 .withPriority(DEFAULT_PRIORITY)
213 .fromApp(applicationId)
214 .makePermanent()
215 .forTable(VLAN_TABLE).build();
216 rules.add(rule);
217 }
Charles Chanf57a8252016-06-29 19:12:37 -0700218
Saurav Das2857f382015-11-03 14:39:27 -0800219 return rules;
220 }
221
Pier Ventree0ae7a32016-11-23 09:57:42 -0800222 /**
Charles Chan0f43e472017-02-14 14:00:16 -0800223 * Creates punt table entry that matches IN_PORT and VLAN_VID and points to
224 * a group that pop vlan and punt.
225 *
226 * @param portNumber port number
227 * @param assignedVlan internally assigned vlan id
228 * @return punt table flow rule
229 */
230 private FlowRule buildPuntTableRule(PortNumber portNumber, VlanId assignedVlan) {
231 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
232 .matchInPort(portNumber)
233 .matchVlanId(assignedVlan);
234 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
Yi Tsengfa394de2017-02-01 11:26:40 -0800235 .group(new GroupId(POP_VLAN_PUNT_GROUP_ID));
Charles Chan0f43e472017-02-14 14:00:16 -0800236
237 return DefaultFlowRule.builder()
238 .forDevice(deviceId)
239 .withSelector(sbuilder.build())
240 .withTreatment(tbuilder.build())
241 .withPriority(PacketPriority.CONTROL.priorityValue())
242 .fromApp(driverId)
243 .makePermanent()
244 .forTable(PUNT_TABLE).build();
245 }
246
247 /**
Pier Ventree0ae7a32016-11-23 09:57:42 -0800248 * Builds a punt to the controller rule for the arp protocol.
Charles Chan0f43e472017-02-14 14:00:16 -0800249 * <p>
250 * NOTE: CpqD cannot punt correctly in group bucket. The current impl will
251 * pop VLAN before sending to controller disregarding whether
252 * it's an internally assigned VLAN or a natural VLAN.
253 * Therefore, trunk port is not supported in CpqD.
Pier Ventree0ae7a32016-11-23 09:57:42 -0800254 *
255 * @param assignedVlan the internal assigned vlan id
256 * @param applicationId the application id
257 * @return the punt flow rule for the arp
258 */
259 private FlowRule buildArpPunt(VlanId assignedVlan, ApplicationId applicationId) {
260 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
261 .matchEthType(Ethernet.TYPE_ARP)
262 .matchVlanId(assignedVlan);
263 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
264 .popVlan()
265 .punt();
266
267 return DefaultFlowRule.builder()
268 .forDevice(deviceId)
269 .withSelector(sbuilder.build())
270 .withTreatment(tbuilder.build())
271 .withPriority(PacketPriority.CONTROL.priorityValue() + 1)
272 .fromApp(applicationId)
273 .makePermanent()
274 .forTable(ACL_TABLE).build();
275 }
276
277 /**
278 * Builds a punt to the controller rule for the icmp v6 messages.
Charles Chan0f43e472017-02-14 14:00:16 -0800279 * <p>
280 * NOTE: CpqD cannot punt correctly in group bucket. The current impl will
281 * pop VLAN before sending to controller disregarding whether
282 * it's an internally assigned VLAN or a natural VLAN.
283 * Therefore, trunk port is not supported in CpqD.
Pier Ventree0ae7a32016-11-23 09:57:42 -0800284 *
285 * @param assignedVlan the internal assigned vlan id
286 * @param applicationId the application id
287 * @return the punt flow rule for the icmp v6 messages
288 */
289 private FlowRule buildIcmpV6Punt(VlanId assignedVlan, ApplicationId applicationId) {
290 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
291 .matchVlanId(assignedVlan)
292 .matchEthType(Ethernet.TYPE_IPV6)
293 .matchIPProtocol(PROTOCOL_ICMP6);
294 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
295 .popVlan()
296 .punt();
297
298 return DefaultFlowRule.builder()
299 .forDevice(deviceId)
300 .withSelector(sbuilder.build())
301 .withTreatment(tbuilder.build())
302 .withPriority(PacketPriority.CONTROL.priorityValue() + 1)
303 .fromApp(applicationId)
304 .makePermanent()
305 .forTable(ACL_TABLE).build();
306 }
307
Saurav Das4ce45962015-11-24 23:21:05 -0800308 /*
309 * Cpqd emulation does not handle vlan tags and mpls labels correctly.
310 * Workaround requires popping off the VLAN tags in the TMAC table.
311 *
312 * (non-Javadoc)
313 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthDstFilter
314 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800315 @Override
Saurav Das4ce45962015-11-24 23:21:05 -0800316 protected List<FlowRule> processEthDstFilter(PortCriterion portCriterion,
317 EthCriterion ethCriterion,
318 VlanIdCriterion vidCriterion,
319 VlanId assignedVlan,
320 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800321 // Consider PortNumber.ANY as wildcard. Match ETH_DST only
322 if (portCriterion != null && portCriterion.port() == PortNumber.ANY) {
323 return processEthDstOnlyFilter(ethCriterion, applicationId);
324 }
325
Charles Chan5b9df8d2016-03-28 22:21:40 -0700326 // Multicast MAC
327 if (ethCriterion.mask() != null) {
328 return processMcastEthDstFilter(ethCriterion, applicationId);
329 }
330
Saurav Das4ce45962015-11-24 23:21:05 -0800331 //handling untagged packets via assigned VLAN
332 if (vidCriterion.vlanId() == VlanId.NONE) {
333 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
334 }
335 // ofdpa cannot match on ALL portnumber, so we need to use separate
336 // rules for each port.
337 List<PortNumber> portnums = new ArrayList<PortNumber>();
338 if (portCriterion.port() == PortNumber.ALL) {
339 for (Port port : deviceService.getPorts(deviceId)) {
340 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
341 portnums.add(port.number());
342 }
343 }
344 } else {
345 portnums.add(portCriterion.port());
346 }
347
348 List<FlowRule> rules = new ArrayList<FlowRule>();
349 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800350 // TMAC rules for unicast IP packets
Saurav Das4ce45962015-11-24 23:21:05 -0800351 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
352 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
353 selector.matchInPort(pnum);
354 selector.matchVlanId(vidCriterion.vlanId());
355 selector.matchEthType(Ethernet.TYPE_IPV4);
356 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800357 if (!supportTaggedMpls()) {
358 treatment.popVlan();
359 }
Saurav Das4ce45962015-11-24 23:21:05 -0800360 treatment.transition(UNICAST_ROUTING_TABLE);
361 FlowRule rule = DefaultFlowRule.builder()
362 .forDevice(deviceId)
363 .withSelector(selector.build())
364 .withTreatment(treatment.build())
365 .withPriority(DEFAULT_PRIORITY)
366 .fromApp(applicationId)
367 .makePermanent()
368 .forTable(TMAC_TABLE).build();
369 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800370
371 // TMAC rules for MPLS packets
Saurav Das4ce45962015-11-24 23:21:05 -0800372 selector = DefaultTrafficSelector.builder();
373 treatment = DefaultTrafficTreatment.builder();
374 selector.matchInPort(pnum);
375 selector.matchVlanId(vidCriterion.vlanId());
376 selector.matchEthType(Ethernet.MPLS_UNICAST);
377 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800378 if (!supportTaggedMpls()) {
379 treatment.popVlan();
380 }
Saurav Das4ce45962015-11-24 23:21:05 -0800381 treatment.transition(MPLS_TABLE_0);
382 rule = DefaultFlowRule.builder()
383 .forDevice(deviceId)
384 .withSelector(selector.build())
385 .withTreatment(treatment.build())
386 .withPriority(DEFAULT_PRIORITY)
387 .fromApp(applicationId)
388 .makePermanent()
389 .forTable(TMAC_TABLE).build();
390 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800391
392 // TMAC rules for IPv6 packets
Pier Ventree0ae7a32016-11-23 09:57:42 -0800393 selector = DefaultTrafficSelector.builder();
394 treatment = DefaultTrafficTreatment.builder();
395 selector.matchInPort(pnum);
396 selector.matchVlanId(vidCriterion.vlanId());
397 selector.matchEthType(Ethernet.TYPE_IPV6);
398 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800399 if (!supportTaggedMpls()) {
400 treatment.popVlan();
401 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800402 treatment.transition(UNICAST_ROUTING_TABLE);
403 rule = DefaultFlowRule.builder()
404 .forDevice(deviceId)
405 .withSelector(selector.build())
406 .withTreatment(treatment.build())
407 .withPriority(DEFAULT_PRIORITY)
408 .fromApp(applicationId)
409 .makePermanent()
410 .forTable(TMAC_TABLE).build();
411 rules.add(rule);
Saurav Das4ce45962015-11-24 23:21:05 -0800412 }
413 return rules;
414 }
415
Charles Chan5270ed02016-01-30 23:22:37 -0800416 @Override
417 protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700418 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800419 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
420 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
421 selector.matchEthType(Ethernet.TYPE_IPV4);
422 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800423 if (!supportTaggedMpls()) {
424 treatment.popVlan();
425 }
Charles Chan5270ed02016-01-30 23:22:37 -0800426 treatment.transition(UNICAST_ROUTING_TABLE);
427 FlowRule rule = DefaultFlowRule.builder()
428 .forDevice(deviceId)
429 .withSelector(selector.build())
430 .withTreatment(treatment.build())
431 .withPriority(DEFAULT_PRIORITY)
432 .fromApp(applicationId)
433 .makePermanent()
434 .forTable(TMAC_TABLE).build();
435 return ImmutableList.<FlowRule>builder().add(rule).build();
436 }
437
Saurav Das4ce45962015-11-24 23:21:05 -0800438 /*
439 * Cpqd emulation allows MPLS ecmp.
440 *
441 * (non-Javadoc)
442 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthTypeSpecific
443 */
444 @Override
445 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800446 TrafficSelector selector = fwd.selector();
447 EthTypeCriterion ethType =
448 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
449 if ((ethType == null) ||
450 (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
Pier Ventree0ae7a32016-11-23 09:57:42 -0800451 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST) &&
452 (ethType.ethType().toShort() != Ethernet.TYPE_IPV6)) {
Saurav Das4ce45962015-11-24 23:21:05 -0800453 log.warn("processSpecific: Unsupported forwarding objective criteria"
454 + "ethType:{} in dev:{}", ethType, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800455 fail(fwd, ObjectiveError.UNSUPPORTED);
456 return Collections.emptySet();
457 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800458 boolean defaultRule = false;
Saurav Das8a0732e2015-11-20 15:27:53 -0800459 int forTableId = -1;
460 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
Flavio Castroe10fa242016-01-15 12:43:51 -0800461 TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder();
462
Saurav Das8a0732e2015-11-20 15:27:53 -0800463 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Flavio Castroe10fa242016-01-15 12:43:51 -0800464 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700465 if (ipv4Dst.isMulticast()) {
466 if (ipv4Dst.prefixLength() != 32) {
467 log.warn("Multicast specific forwarding objective can only be /32");
468 fail(fwd, ObjectiveError.BADPARAMS);
469 return ImmutableSet.of();
470 }
471 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
472 if (assignedVlan == null) {
473 log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
474 fail(fwd, ObjectiveError.BADPARAMS);
475 return ImmutableSet.of();
476 }
477 filteredSelector.matchVlanId(assignedVlan);
478 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
479 forTableId = MULTICAST_ROUTING_TABLE;
480 log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
481 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800482 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700483 if (ipv4Dst.prefixLength() == 0) {
484 // The entire IPV4_DST field is wildcarded intentionally
485 filteredSelector.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700486 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700487 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700488 }
489 forTableId = UNICAST_ROUTING_TABLE;
490 log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
491 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800492 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800493 } else if (ethType.ethType().toShort() == Ethernet.TYPE_IPV6) {
494 if (buildIpv6Selector(filteredSelector, fwd) < 0) {
495 return Collections.emptyList();
496 }
497 forTableId = UNICAST_ROUTING_TABLE;
Saurav Das8a0732e2015-11-20 15:27:53 -0800498 } else {
499 filteredSelector
500 .matchEthType(Ethernet.MPLS_UNICAST)
501 .matchMplsLabel(((MplsCriterion)
502 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
503 MplsBosCriterion bos = (MplsBosCriterion) selector
504 .getCriterion(Criterion.Type.MPLS_BOS);
505 if (bos != null) {
506 filteredSelector.matchMplsBos(bos.mplsBos());
507 }
508 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800509 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
510 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800511 }
512
513 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
514 if (fwd.treatment() != null) {
515 for (Instruction i : fwd.treatment().allInstructions()) {
Charles Chan40132b32017-01-22 00:19:37 -0800516 if (!supportCopyTtl() && i instanceof L3ModificationInstruction) {
517 L3ModificationInstruction l3instr = (L3ModificationInstruction) i;
518 if (l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_IN) ||
519 l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_OUT)) {
520 continue;
521 }
522 }
Charles Chan7d10b162015-12-07 18:54:45 -0800523 /*
524 * NOTE: OF-DPA does not support immediate instruction in
525 * L3 unicast and MPLS table.
526 */
527 tb.deferred().add(i);
Saurav Das8a0732e2015-11-20 15:27:53 -0800528 }
529 }
530
531 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800532 NextGroup next = getGroupForNextObjective(fwd.nextId());
533 if (next != null) {
534 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
535 // we only need the top level group's key to point the flow to it
536 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
537 if (group == null) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700538 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
539 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800540 fail(fwd, ObjectiveError.GROUPMISSING);
541 return Collections.emptySet();
542 }
543 tb.deferred().group(group.id());
Saurav Das8a0732e2015-11-20 15:27:53 -0800544 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800545 }
546 tb.transition(ACL_TABLE);
547 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
548 .fromApp(fwd.appId())
549 .withPriority(fwd.priority())
550 .forDevice(deviceId)
551 .withSelector(filteredSelector.build())
552 .withTreatment(tb.build())
553 .forTable(forTableId);
554
555 if (fwd.permanent()) {
556 ruleBuilder.makePermanent();
557 } else {
558 ruleBuilder.makeTemporary(fwd.timeout());
559 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800560 Collection<FlowRule> flowRuleCollection = new ArrayList<>();
561 flowRuleCollection.add(ruleBuilder.build());
562 if (defaultRule) {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800563 flowRuleCollection.add(
564 defaultRoute(fwd, complementarySelector, forTableId, tb)
565 );
Flavio Castroe10fa242016-01-15 12:43:51 -0800566 log.debug("Default rule 0.0.0.0/0 is being installed two rules");
567 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800568 return flowRuleCollection;
Saurav Das8a0732e2015-11-20 15:27:53 -0800569 }
570
Charles Chan1e492d32016-01-30 23:22:37 -0800571 @Override
572 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
573 List<FlowRule> rules = new ArrayList<>();
574
575 // Build filtered selector
576 TrafficSelector selector = fwd.selector();
577 EthCriterion ethCriterion = (EthCriterion) selector
578 .getCriterion(Criterion.Type.ETH_DST);
579 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
580 .getCriterion(Criterion.Type.VLAN_VID);
581
582 if (vlanIdCriterion == null) {
583 log.warn("Forwarding objective for bridging requires vlan. Not "
584 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
585 fail(fwd, ObjectiveError.BADPARAMS);
586 return Collections.emptySet();
587 }
588
589 TrafficSelector.Builder filteredSelectorBuilder =
590 DefaultTrafficSelector.builder();
591 // Do not match MacAddress for subnet broadcast entry
Charles Chand05f54b2017-02-13 11:56:54 -0800592 if (!ethCriterion.mac().equals(NONE) && !ethCriterion.mac().equals(BROADCAST)) {
Charles Chan1e492d32016-01-30 23:22:37 -0800593 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
594 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
595 fwd.id(), fwd.nextId(), deviceId);
596 } else {
597 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
598 + "in dev:{} for vlan:{}",
599 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
600 }
601 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
602 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
603
604 if (fwd.treatment() != null) {
605 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
606 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
607 }
608
609 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
610 if (fwd.nextId() != null) {
611 NextGroup next = getGroupForNextObjective(fwd.nextId());
612 if (next != null) {
613 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
614 // we only need the top level group's key to point the flow to it
615 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
616 if (group != null) {
617 treatmentBuilder.deferred().group(group.id());
618 } else {
619 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
620 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
621 fail(fwd, ObjectiveError.GROUPMISSING);
622 return Collections.emptySet();
623 }
624 }
625 }
626 treatmentBuilder.immediate().transition(ACL_TABLE);
627 TrafficTreatment filteredTreatment = treatmentBuilder.build();
628
629 // Build bridging table entries
630 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
631 flowRuleBuilder.fromApp(fwd.appId())
632 .withPriority(fwd.priority())
633 .forDevice(deviceId)
634 .withSelector(filteredSelector)
635 .withTreatment(filteredTreatment)
636 .forTable(BRIDGING_TABLE);
637 if (fwd.permanent()) {
638 flowRuleBuilder.makePermanent();
639 } else {
640 flowRuleBuilder.makeTemporary(fwd.timeout());
641 }
642 rules.add(flowRuleBuilder.build());
643 return rules;
644 }
645
Saurav Das52025962016-01-28 22:30:01 -0800646 /*
647 * In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the
648 * ACL table. Because we pop off vlan tags in TMAC table,
649 * we need to avoid matching on vlans in the ACL table.
650 */
651 @Override
652 protected Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
653 log.info("Processing versatile forwarding objective");
654
655 EthTypeCriterion ethType =
656 (EthTypeCriterion) fwd.selector().getCriterion(Criterion.Type.ETH_TYPE);
657 if (ethType == null) {
658 log.error("Versatile forwarding objective must include ethType");
659 fail(fwd, ObjectiveError.BADPARAMS);
660 return Collections.emptySet();
661 }
662 if (fwd.nextId() == null && fwd.treatment() == null) {
663 log.error("Forwarding objective {} from {} must contain "
664 + "nextId or Treatment", fwd.selector(), fwd.appId());
665 return Collections.emptySet();
666 }
667
668 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
669 fwd.selector().criteria().forEach(criterion -> {
670 if (criterion instanceof VlanIdCriterion) {
671 // avoid matching on vlans
672 return;
Pier Ventree0ae7a32016-11-23 09:57:42 -0800673 } else if (criterion instanceof Icmpv6TypeCriterion ||
674 criterion instanceof Icmpv6CodeCriterion) {
675 /*
676 * We silenty discard these criterions, our current
677 * OFDPA platform does not support these matches on
678 * the ACL table.
679 */
680 log.warn("ICMPv6 Type and ICMPv6 Code are not supported");
Saurav Das52025962016-01-28 22:30:01 -0800681 } else {
682 sbuilder.add(criterion);
683 }
684 });
685
686 // XXX driver does not currently do type checking as per Tables 65-67 in
687 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
688 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
689 if (fwd.treatment() != null) {
690 for (Instruction ins : fwd.treatment().allInstructions()) {
691 if (ins instanceof OutputInstruction) {
692 OutputInstruction o = (OutputInstruction) ins;
693 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan0f43e472017-02-14 14:00:16 -0800694 ttBuilder.transition(PUNT_TABLE);
Saurav Das52025962016-01-28 22:30:01 -0800695 } else {
696 log.warn("Only allowed treatments in versatile forwarding "
697 + "objectives are punts to the controller");
698 }
699 } else {
700 log.warn("Cannot process instruction in versatile fwd {}", ins);
701 }
702 }
Charles Chan2df0e8a2017-01-09 11:45:08 -0800703 if (fwd.treatment().clearedDeferred()) {
704 ttBuilder.wipeDeferred();
705 }
Saurav Das52025962016-01-28 22:30:01 -0800706 }
707 if (fwd.nextId() != null) {
708 // overide case
709 NextGroup next = getGroupForNextObjective(fwd.nextId());
710 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
711 // we only need the top level group's key to point the flow to it
712 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
713 if (group == null) {
714 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
715 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
716 fail(fwd, ObjectiveError.GROUPMISSING);
717 return Collections.emptySet();
718 }
719 ttBuilder.deferred().group(group.id());
720 }
721
722 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
723 .fromApp(fwd.appId())
724 .withPriority(fwd.priority())
725 .forDevice(deviceId)
726 .withSelector(sbuilder.build())
727 .withTreatment(ttBuilder.build())
728 .makePermanent()
729 .forTable(ACL_TABLE);
730 return Collections.singletonList(ruleBuilder.build());
731 }
732
733 /*
734 * Cpqd emulation requires table-miss-entries in forwarding tables.
735 * Real OFDPA does not require these rules as they are put in by default.
736 *
737 * (non-Javadoc)
738 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#initializePipeline()
739 */
Saurav Das2857f382015-11-03 14:39:27 -0800740 @Override
Saurav Das558afec2015-05-31 17:12:48 -0700741 protected void initializePipeline() {
Charles Chanf6ec1532017-02-08 16:10:40 -0800742 initTableMiss(PORT_TABLE, VLAN_TABLE, null);
743 initTableMiss(VLAN_TABLE, ACL_TABLE, null);
744 initTableMiss(TMAC_TABLE, BRIDGING_TABLE, null);
745 initTableMiss(UNICAST_ROUTING_TABLE, ACL_TABLE, null);
746 initTableMiss(MULTICAST_ROUTING_TABLE, ACL_TABLE, null);
747 initTableMiss(MPLS_TABLE_0, MPLS_TABLE_1, null);
748 initTableMiss(MPLS_TABLE_1, ACL_TABLE, null);
749 initTableMiss(BRIDGING_TABLE, ACL_TABLE, null);
750 initTableMiss(ACL_TABLE, -1, null);
Charles Chan0f43e472017-02-14 14:00:16 -0800751
752 if (supportPuntGroup()) {
753 initTableMiss(PUNT_TABLE, -1,
754 DefaultTrafficTreatment.builder().punt().build());
755 initPopVlanPuntGroup();
756 } else {
757 initTableMiss(PUNT_TABLE, -1,
758 DefaultTrafficTreatment.builder().popVlan().punt().build());
759 }
Saurav Das558afec2015-05-31 17:12:48 -0700760 }
761
Charles Chanf6ec1532017-02-08 16:10:40 -0800762 /**
763 * Install table-miss flow entry.
764 *
765 * If treatment exists, use it directly.
766 * Else if treatment does not exist but nextTable > 0, transit to next table.
767 * Else apply empty treatment.
768 *
769 * @param thisTable this table ID
770 * @param nextTable next table ID
771 * @param treatment traffic treatment to apply.
772 */
773 private void initTableMiss(int thisTable, int nextTable, TrafficTreatment treatment) {
Saurav Das558afec2015-05-31 17:12:48 -0700774 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chanf6ec1532017-02-08 16:10:40 -0800775 TrafficSelector selector = DefaultTrafficSelector.builder().build();
Saurav Das558afec2015-05-31 17:12:48 -0700776
Charles Chanf6ec1532017-02-08 16:10:40 -0800777 if (treatment == null) {
778 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
779 if (nextTable > 0) {
780 tBuilder.transition(nextTable);
Saurav Das558afec2015-05-31 17:12:48 -0700781 }
Charles Chanf6ec1532017-02-08 16:10:40 -0800782 treatment = tBuilder.build();
783 }
Saurav Das558afec2015-05-31 17:12:48 -0700784
Charles Chanb7504392017-02-10 12:51:04 -0800785 FlowRule rule = DefaultFlowRule.builder()
786 .forDevice(deviceId)
Charles Chanf6ec1532017-02-08 16:10:40 -0800787 .withSelector(selector)
788 .withTreatment(treatment)
Charles Chanb7504392017-02-10 12:51:04 -0800789 .withPriority(LOWEST_PRIORITY)
790 .fromApp(driverId)
791 .makePermanent()
Charles Chanf6ec1532017-02-08 16:10:40 -0800792 .forTable(thisTable).build();
Charles Chanb7504392017-02-10 12:51:04 -0800793 ops = ops.add(rule);
Saurav Das2857f382015-11-03 14:39:27 -0800794
795 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
796 @Override
797 public void onSuccess(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -0800798 log.info("Initialized table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -0800799 }
Saurav Das2857f382015-11-03 14:39:27 -0800800 @Override
801 public void onError(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -0800802 log.warn("Failed to initialize table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -0800803 }
804 }));
805 }
Charles Chan0f43e472017-02-14 14:00:16 -0800806
807 /**
808 * Builds a indirect group contains pop_vlan and punt actions.
809 * <p>
810 * Using group instead of immediate action to ensure that
811 * the copy of packet on the data plane is not affected by the pop vlan action.
812 */
813 private void initPopVlanPuntGroup() {
Yi Tsengef19de12017-04-24 11:33:05 -0700814 GroupKey groupKey = popVlanPuntGroupKey();
Charles Chan0f43e472017-02-14 14:00:16 -0800815 TrafficTreatment bucketTreatment = DefaultTrafficTreatment.builder()
816 .popVlan().punt().build();
817 GroupBucket bucket =
818 DefaultGroupBucket.createIndirectGroupBucket(bucketTreatment);
819 GroupDescription groupDesc =
820 new DefaultGroupDescription(
821 deviceId,
822 GroupDescription.Type.INDIRECT,
823 new GroupBuckets(Collections.singletonList(bucket)),
824 groupKey,
825 POP_VLAN_PUNT_GROUP_ID,
826 driverId);
827 groupService.addGroup(groupDesc);
828
829 log.info("Initialized pop vlan punt group on {}", deviceId);
830 }
Yi Tsengef19de12017-04-24 11:33:05 -0700831
832 /**
833 * Generates group key for a static indirect group that pop vlan and punt to
834 * controller.
835 *
836 * @return the group key of the indirect table
837 */
838 private GroupKey popVlanPuntGroupKey() {
839 int hash = POP_VLAN_PUNT_GROUP_ID | (Objects.hash(deviceId) & FOUR_BIT_MASK);
840 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
841 }
Saurav Das558afec2015-05-31 17:12:48 -0700842}