blob: d8de983bb483f0db18e516fe118d965f0efe49b4 [file] [log] [blame]
Thomas Vachuska58de4162015-09-10 16:15:33 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Thomas Vachuska58de4162015-09-10 16:15:33 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Yi Tsengef19de12017-04-24 11:33:05 -070016package org.onosproject.driver.pipeline.ofdpa;
Saurav Das558afec2015-05-31 17:12:48 -070017
Charles Chan5270ed02016-01-30 23:22:37 -080018import com.google.common.collect.ImmutableList;
Charles Chan5b9df8d2016-03-28 22:21:40 -070019import com.google.common.collect.ImmutableSet;
Saurav Das8a0732e2015-11-20 15:27:53 -080020import org.onlab.packet.Ethernet;
Julia Fergusond8f145e2017-08-10 18:15:24 +000021import org.onlab.packet.IpAddress;
Flavio Castroe10fa242016-01-15 12:43:51 -080022import org.onlab.packet.IpPrefix;
Saurav Das2857f382015-11-03 14:39:27 -080023import org.onlab.packet.VlanId;
24import org.onosproject.core.ApplicationId;
Yi Tsengfa394de2017-02-01 11:26:40 -080025import org.onosproject.core.GroupId;
Saurav Das2857f382015-11-03 14:39:27 -080026import org.onosproject.net.Port;
27import org.onosproject.net.PortNumber;
Saurav Das8a0732e2015-11-20 15:27:53 -080028import org.onosproject.net.behaviour.NextGroup;
Charles Chan425854b2016-04-11 15:32:12 -070029import org.onosproject.net.behaviour.PipelinerContext;
Saurav Das558afec2015-05-31 17:12:48 -070030import org.onosproject.net.flow.DefaultFlowRule;
31import org.onosproject.net.flow.DefaultTrafficSelector;
32import org.onosproject.net.flow.DefaultTrafficTreatment;
33import org.onosproject.net.flow.FlowRule;
34import org.onosproject.net.flow.FlowRuleOperations;
35import org.onosproject.net.flow.FlowRuleOperationsContext;
36import org.onosproject.net.flow.TrafficSelector;
37import org.onosproject.net.flow.TrafficTreatment;
Saurav Das4ce45962015-11-24 23:21:05 -080038import org.onosproject.net.flow.criteria.Criteria;
Saurav Das8a0732e2015-11-20 15:27:53 -080039import org.onosproject.net.flow.criteria.Criterion;
Saurav Das4ce45962015-11-24 23:21:05 -080040import org.onosproject.net.flow.criteria.EthCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080041import org.onosproject.net.flow.criteria.EthTypeCriterion;
42import org.onosproject.net.flow.criteria.IPCriterion;
Pier Ventree0ae7a32016-11-23 09:57:42 -080043import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion;
44import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080045import org.onosproject.net.flow.criteria.MplsBosCriterion;
46import org.onosproject.net.flow.criteria.MplsCriterion;
Saurav Das2857f382015-11-03 14:39:27 -080047import org.onosproject.net.flow.criteria.PortCriterion;
48import org.onosproject.net.flow.criteria.VlanIdCriterion;
Saurav Das8a0732e2015-11-20 15:27:53 -080049import org.onosproject.net.flow.instructions.Instruction;
Saurav Das52025962016-01-28 22:30:01 -080050import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Charles Chan40132b32017-01-22 00:19:37 -080051import org.onosproject.net.flow.instructions.L3ModificationInstruction;
Saurav Das8a0732e2015-11-20 15:27:53 -080052import org.onosproject.net.flowobjective.ForwardingObjective;
53import org.onosproject.net.flowobjective.ObjectiveError;
Charles Chan0f43e472017-02-14 14:00:16 -080054import org.onosproject.net.group.DefaultGroupBucket;
55import org.onosproject.net.group.DefaultGroupDescription;
56import org.onosproject.net.group.DefaultGroupKey;
Saurav Das8a0732e2015-11-20 15:27:53 -080057import org.onosproject.net.group.Group;
Charles Chan0f43e472017-02-14 14:00:16 -080058import org.onosproject.net.group.GroupBucket;
59import org.onosproject.net.group.GroupBuckets;
60import org.onosproject.net.group.GroupDescription;
Saurav Das8a0732e2015-11-20 15:27:53 -080061import org.onosproject.net.group.GroupKey;
Charles Chanf57a8252016-06-29 19:12:37 -070062import org.onosproject.net.packet.PacketPriority;
Saurav Das558afec2015-05-31 17:12:48 -070063import org.slf4j.Logger;
64
Jonathan Hart855179c2016-04-26 07:40:04 -070065import java.util.ArrayList;
66import java.util.Collection;
67import java.util.Collections;
68import java.util.Deque;
69import java.util.List;
Charles Chan0f43e472017-02-14 14:00:16 -080070import java.util.Objects;
Jonathan Hart855179c2016-04-26 07:40:04 -070071
Pier Ventree0ae7a32016-11-23 09:57:42 -080072import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
Charles Chand05f54b2017-02-13 11:56:54 -080073import static org.onlab.packet.MacAddress.BROADCAST;
74import static org.onlab.packet.MacAddress.NONE;
Yi Tsengef19de12017-04-24 11:33:05 -070075import static org.onosproject.driver.pipeline.ofdpa.OfdpaGroupHandlerUtility.*;
Jonathan Hart855179c2016-04-26 07:40:04 -070076import static org.slf4j.LoggerFactory.getLogger;
77
Saurav Das558afec2015-05-31 17:12:48 -070078
79/**
Charles Chan40132b32017-01-22 00:19:37 -080080 * Driver for software switch emulation of the OFDPA pipeline.
Saurav Das52025962016-01-28 22:30:01 -080081 * The software switch is the CPqD OF 1.3 switch. Unfortunately the CPqD switch
82 * does not handle vlan tags and mpls labels simultaneously, which requires us
83 * to do some workarounds in the driver. This driver is meant for the use of
84 * the cpqd switch when MPLS is required. As a result this driver works only
85 * on incoming untagged packets.
Saurav Das558afec2015-05-31 17:12:48 -070086 */
Charles Chan361154b2016-03-24 10:23:39 -070087public class CpqdOfdpa2Pipeline extends Ofdpa2Pipeline {
Saurav Das558afec2015-05-31 17:12:48 -070088
89 private final Logger log = getLogger(getClass());
90
Charles Chan40132b32017-01-22 00:19:37 -080091 /**
Charles Chan0f43e472017-02-14 14:00:16 -080092 * Table that determines whether VLAN is popped before punting to controller.
93 * <p>
94 * This is a non-OFDPA table to emulate OFDPA packet in behavior.
95 * VLAN will be popped before punting if the VLAN is internally assigned.
96 * <p>
97 * Also note that 63 is the max table number in CpqD.
98 */
99 private static final int PUNT_TABLE = 63;
100
101 /**
102 * A static indirect group that pop vlan and punt to controller.
103 * <p>
104 * The purpose of using a group instead of immediate action is that this
105 * won't affect another copy on the data plane when write action exists.
106 */
107 private static final int POP_VLAN_PUNT_GROUP_ID = 0xc0000000;
108
Charles Chan053b1cb2017-03-22 16:56:35 -0700109 @Override
110 protected boolean requireVlanExtensions() {
111 return false;
112 }
113
Charles Chan0f43e472017-02-14 14:00:16 -0800114 /**
Charles Chan40132b32017-01-22 00:19:37 -0800115 * Determines whether this pipeline support copy ttl instructions or not.
116 *
117 * @return true if copy ttl instructions are supported
118 */
119 protected boolean supportCopyTtl() {
120 return true;
121 }
122
Charles Chan0f43e472017-02-14 14:00:16 -0800123 /**
124 * Determines whether this pipeline support push mpls to vlan-tagged packets or not.
125 * <p>
126 * If not support, pop vlan before push entering unicast and mpls table.
127 * Side effect: HostService learns redundant hosts with same MAC but
128 * different VLAN. No known side effect on the network reachability.
129 *
130 * @return true if push mpls to vlan-tagged packets is supported
131 */
132 protected boolean supportTaggedMpls() {
133 return false;
134 }
135
136 /**
137 * Determines whether this pipeline support punt action in group bucket.
138 *
139 * @return true if punt action in group bucket is supported
140 */
141 protected boolean supportPuntGroup() {
142 return false;
143 }
144
Charles Chan425854b2016-04-11 15:32:12 -0700145 @Override
Charles Chan40132b32017-01-22 00:19:37 -0800146 protected void initDriverId() {
Charles Chan425854b2016-04-11 15:32:12 -0700147 driverId = coreService.registerApplication(
148 "org.onosproject.driver.CpqdOfdpa2Pipeline");
Charles Chan40132b32017-01-22 00:19:37 -0800149 }
Charles Chan425854b2016-04-11 15:32:12 -0700150
Charles Chan40132b32017-01-22 00:19:37 -0800151 @Override
152 protected void initGroupHander(PipelinerContext context) {
153 groupHandler = new CpqdOfdpa2GroupHandler();
154 groupHandler.init(deviceId, context);
Charles Chan425854b2016-04-11 15:32:12 -0700155 }
156
Saurav Das4ce45962015-11-24 23:21:05 -0800157 /*
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700158 * Cpqd emulation does not require the non OF-standard rules for
159 * matching untagged packets that ofdpa uses.
Saurav Das4ce45962015-11-24 23:21:05 -0800160 *
161 * (non-Javadoc)
162 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processVlanIdFilter
163 */
Saurav Das558afec2015-05-31 17:12:48 -0700164 @Override
Saurav Das2857f382015-11-03 14:39:27 -0800165 protected List<FlowRule> processVlanIdFilter(PortCriterion portCriterion,
166 VlanIdCriterion vidCriterion,
167 VlanId assignedVlan,
168 ApplicationId applicationId) {
Charles Chan79769232016-07-05 16:34:39 -0700169 List<FlowRule> rules = new ArrayList<>();
Saurav Das2857f382015-11-03 14:39:27 -0800170 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
171 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
172 selector.matchVlanId(vidCriterion.vlanId());
Saurav Das4f980082015-11-05 13:39:15 -0800173 treatment.transition(TMAC_TABLE);
174
Saurav Das2857f382015-11-03 14:39:27 -0800175 if (vidCriterion.vlanId() == VlanId.NONE) {
176 // untagged packets are assigned vlans
177 treatment.pushVlan().setVlanId(assignedVlan);
178 }
Saurav Das2857f382015-11-03 14:39:27 -0800179
180 // ofdpa cannot match on ALL portnumber, so we need to use separate
181 // rules for each port.
182 List<PortNumber> portnums = new ArrayList<PortNumber>();
183 if (portCriterion.port() == PortNumber.ALL) {
184 for (Port port : deviceService.getPorts(deviceId)) {
185 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
186 portnums.add(port.number());
187 }
188 }
189 } else {
190 portnums.add(portCriterion.port());
191 }
Saurav Das4f980082015-11-05 13:39:15 -0800192
Saurav Das2857f382015-11-03 14:39:27 -0800193 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800194 // NOTE: Emulating OFDPA behavior by popping off internal assigned
195 // VLAN before sending to controller
196 if (supportPuntGroup() && vidCriterion.vlanId() == VlanId.NONE) {
Yi Tsengef19de12017-04-24 11:33:05 -0700197 GroupKey groupKey = popVlanPuntGroupKey();
Charles Chan0f43e472017-02-14 14:00:16 -0800198 Group group = groupService.getGroup(deviceId, groupKey);
199 if (group != null) {
200 rules.add(buildPuntTableRule(pnum, assignedVlan));
201 } else {
202 log.info("popVlanPuntGroup not found in dev:{}", deviceId);
203 return Collections.emptyList();
204 }
205 }
206
Saurav Das4f980082015-11-05 13:39:15 -0800207 // create rest of flowrule
Saurav Das2857f382015-11-03 14:39:27 -0800208 selector.matchInPort(pnum);
209 FlowRule rule = DefaultFlowRule.builder()
210 .forDevice(deviceId)
211 .withSelector(selector.build())
212 .withTreatment(treatment.build())
213 .withPriority(DEFAULT_PRIORITY)
214 .fromApp(applicationId)
215 .makePermanent()
216 .forTable(VLAN_TABLE).build();
217 rules.add(rule);
218 }
Charles Chanf57a8252016-06-29 19:12:37 -0700219
Saurav Das2857f382015-11-03 14:39:27 -0800220 return rules;
221 }
222
Pier Ventree0ae7a32016-11-23 09:57:42 -0800223 /**
Charles Chan0f43e472017-02-14 14:00:16 -0800224 * Creates punt table entry that matches IN_PORT and VLAN_VID and points to
225 * a group that pop vlan and punt.
226 *
227 * @param portNumber port number
228 * @param assignedVlan internally assigned vlan id
229 * @return punt table flow rule
230 */
231 private FlowRule buildPuntTableRule(PortNumber portNumber, VlanId assignedVlan) {
232 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
233 .matchInPort(portNumber)
234 .matchVlanId(assignedVlan);
235 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
Yi Tsengfa394de2017-02-01 11:26:40 -0800236 .group(new GroupId(POP_VLAN_PUNT_GROUP_ID));
Charles Chan0f43e472017-02-14 14:00:16 -0800237
238 return DefaultFlowRule.builder()
239 .forDevice(deviceId)
240 .withSelector(sbuilder.build())
241 .withTreatment(tbuilder.build())
242 .withPriority(PacketPriority.CONTROL.priorityValue())
243 .fromApp(driverId)
244 .makePermanent()
245 .forTable(PUNT_TABLE).build();
246 }
247
248 /**
Pier Ventree0ae7a32016-11-23 09:57:42 -0800249 * Builds a punt to the controller rule for the arp protocol.
Charles Chan0f43e472017-02-14 14:00:16 -0800250 * <p>
251 * NOTE: CpqD cannot punt correctly in group bucket. The current impl will
252 * pop VLAN before sending to controller disregarding whether
253 * it's an internally assigned VLAN or a natural VLAN.
254 * Therefore, trunk port is not supported in CpqD.
Pier Ventree0ae7a32016-11-23 09:57:42 -0800255 *
256 * @param assignedVlan the internal assigned vlan id
257 * @param applicationId the application id
258 * @return the punt flow rule for the arp
259 */
260 private FlowRule buildArpPunt(VlanId assignedVlan, ApplicationId applicationId) {
261 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
262 .matchEthType(Ethernet.TYPE_ARP)
263 .matchVlanId(assignedVlan);
264 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
265 .popVlan()
266 .punt();
267
268 return DefaultFlowRule.builder()
269 .forDevice(deviceId)
270 .withSelector(sbuilder.build())
271 .withTreatment(tbuilder.build())
272 .withPriority(PacketPriority.CONTROL.priorityValue() + 1)
273 .fromApp(applicationId)
274 .makePermanent()
275 .forTable(ACL_TABLE).build();
276 }
277
278 /**
279 * Builds a punt to the controller rule for the icmp v6 messages.
Charles Chan0f43e472017-02-14 14:00:16 -0800280 * <p>
281 * NOTE: CpqD cannot punt correctly in group bucket. The current impl will
282 * pop VLAN before sending to controller disregarding whether
283 * it's an internally assigned VLAN or a natural VLAN.
284 * Therefore, trunk port is not supported in CpqD.
Pier Ventree0ae7a32016-11-23 09:57:42 -0800285 *
286 * @param assignedVlan the internal assigned vlan id
287 * @param applicationId the application id
288 * @return the punt flow rule for the icmp v6 messages
289 */
290 private FlowRule buildIcmpV6Punt(VlanId assignedVlan, ApplicationId applicationId) {
291 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder()
292 .matchVlanId(assignedVlan)
293 .matchEthType(Ethernet.TYPE_IPV6)
294 .matchIPProtocol(PROTOCOL_ICMP6);
295 TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder()
296 .popVlan()
297 .punt();
298
299 return DefaultFlowRule.builder()
300 .forDevice(deviceId)
301 .withSelector(sbuilder.build())
302 .withTreatment(tbuilder.build())
303 .withPriority(PacketPriority.CONTROL.priorityValue() + 1)
304 .fromApp(applicationId)
305 .makePermanent()
306 .forTable(ACL_TABLE).build();
307 }
308
Saurav Das4ce45962015-11-24 23:21:05 -0800309 /*
310 * Cpqd emulation does not handle vlan tags and mpls labels correctly.
311 * Workaround requires popping off the VLAN tags in the TMAC table.
312 *
313 * (non-Javadoc)
314 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthDstFilter
315 */
Saurav Das8a0732e2015-11-20 15:27:53 -0800316 @Override
Saurav Das4ce45962015-11-24 23:21:05 -0800317 protected List<FlowRule> processEthDstFilter(PortCriterion portCriterion,
318 EthCriterion ethCriterion,
319 VlanIdCriterion vidCriterion,
320 VlanId assignedVlan,
321 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800322 // Consider PortNumber.ANY as wildcard. Match ETH_DST only
323 if (portCriterion != null && portCriterion.port() == PortNumber.ANY) {
324 return processEthDstOnlyFilter(ethCriterion, applicationId);
325 }
326
Charles Chan5b9df8d2016-03-28 22:21:40 -0700327 // Multicast MAC
328 if (ethCriterion.mask() != null) {
Charles Chan206cac02017-10-20 19:09:16 -0700329 return processMcastEthDstFilter(ethCriterion, assignedVlan, applicationId);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700330 }
331
Saurav Das4ce45962015-11-24 23:21:05 -0800332 //handling untagged packets via assigned VLAN
333 if (vidCriterion.vlanId() == VlanId.NONE) {
334 vidCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
335 }
336 // ofdpa cannot match on ALL portnumber, so we need to use separate
337 // rules for each port.
338 List<PortNumber> portnums = new ArrayList<PortNumber>();
Ray Milkeyfd4f8d32018-01-17 15:24:52 -0800339 if (portCriterion != null) {
340 if (portCriterion.port() == PortNumber.ALL) {
341 for (Port port : deviceService.getPorts(deviceId)) {
342 if (port.number().toLong() > 0 && port.number().toLong() < OFPP_MAX) {
343 portnums.add(port.number());
344 }
Saurav Das4ce45962015-11-24 23:21:05 -0800345 }
Ray Milkeyfd4f8d32018-01-17 15:24:52 -0800346 } else {
347 portnums.add(portCriterion.port());
Saurav Das4ce45962015-11-24 23:21:05 -0800348 }
Saurav Das4ce45962015-11-24 23:21:05 -0800349 }
350
351 List<FlowRule> rules = new ArrayList<FlowRule>();
352 for (PortNumber pnum : portnums) {
Charles Chan0f43e472017-02-14 14:00:16 -0800353 // TMAC rules for unicast IP packets
Saurav Das4ce45962015-11-24 23:21:05 -0800354 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
355 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
356 selector.matchInPort(pnum);
357 selector.matchVlanId(vidCriterion.vlanId());
358 selector.matchEthType(Ethernet.TYPE_IPV4);
359 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800360 if (!supportTaggedMpls()) {
361 treatment.popVlan();
362 }
Saurav Das4ce45962015-11-24 23:21:05 -0800363 treatment.transition(UNICAST_ROUTING_TABLE);
364 FlowRule rule = DefaultFlowRule.builder()
365 .forDevice(deviceId)
366 .withSelector(selector.build())
367 .withTreatment(treatment.build())
368 .withPriority(DEFAULT_PRIORITY)
369 .fromApp(applicationId)
370 .makePermanent()
371 .forTable(TMAC_TABLE).build();
372 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800373
374 // TMAC rules for MPLS packets
Saurav Das4ce45962015-11-24 23:21:05 -0800375 selector = DefaultTrafficSelector.builder();
376 treatment = DefaultTrafficTreatment.builder();
377 selector.matchInPort(pnum);
378 selector.matchVlanId(vidCriterion.vlanId());
379 selector.matchEthType(Ethernet.MPLS_UNICAST);
380 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800381 if (!supportTaggedMpls()) {
382 treatment.popVlan();
383 }
Saurav Das4ce45962015-11-24 23:21:05 -0800384 treatment.transition(MPLS_TABLE_0);
385 rule = DefaultFlowRule.builder()
386 .forDevice(deviceId)
387 .withSelector(selector.build())
388 .withTreatment(treatment.build())
389 .withPriority(DEFAULT_PRIORITY)
390 .fromApp(applicationId)
391 .makePermanent()
392 .forTable(TMAC_TABLE).build();
393 rules.add(rule);
Charles Chan0f43e472017-02-14 14:00:16 -0800394
395 // TMAC rules for IPv6 packets
Pier Ventree0ae7a32016-11-23 09:57:42 -0800396 selector = DefaultTrafficSelector.builder();
397 treatment = DefaultTrafficTreatment.builder();
398 selector.matchInPort(pnum);
399 selector.matchVlanId(vidCriterion.vlanId());
400 selector.matchEthType(Ethernet.TYPE_IPV6);
401 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800402 if (!supportTaggedMpls()) {
403 treatment.popVlan();
404 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800405 treatment.transition(UNICAST_ROUTING_TABLE);
406 rule = DefaultFlowRule.builder()
407 .forDevice(deviceId)
408 .withSelector(selector.build())
409 .withTreatment(treatment.build())
410 .withPriority(DEFAULT_PRIORITY)
411 .fromApp(applicationId)
412 .makePermanent()
413 .forTable(TMAC_TABLE).build();
414 rules.add(rule);
Saurav Das4ce45962015-11-24 23:21:05 -0800415 }
416 return rules;
417 }
418
Charles Chan5270ed02016-01-30 23:22:37 -0800419 @Override
420 protected List<FlowRule> processEthDstOnlyFilter(EthCriterion ethCriterion,
Yi Tsengef19de12017-04-24 11:33:05 -0700421 ApplicationId applicationId) {
Charles Chan5270ed02016-01-30 23:22:37 -0800422 TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
423 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
424 selector.matchEthType(Ethernet.TYPE_IPV4);
425 selector.matchEthDst(ethCriterion.mac());
Charles Chan0f43e472017-02-14 14:00:16 -0800426 if (!supportTaggedMpls()) {
427 treatment.popVlan();
428 }
Charles Chan5270ed02016-01-30 23:22:37 -0800429 treatment.transition(UNICAST_ROUTING_TABLE);
430 FlowRule rule = DefaultFlowRule.builder()
431 .forDevice(deviceId)
432 .withSelector(selector.build())
433 .withTreatment(treatment.build())
434 .withPriority(DEFAULT_PRIORITY)
435 .fromApp(applicationId)
436 .makePermanent()
437 .forTable(TMAC_TABLE).build();
438 return ImmutableList.<FlowRule>builder().add(rule).build();
439 }
440
Saurav Das4ce45962015-11-24 23:21:05 -0800441 /*
442 * Cpqd emulation allows MPLS ecmp.
443 *
444 * (non-Javadoc)
445 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#processEthTypeSpecific
446 */
447 @Override
448 protected Collection<FlowRule> processEthTypeSpecific(ForwardingObjective fwd) {
Saurav Das8a0732e2015-11-20 15:27:53 -0800449 TrafficSelector selector = fwd.selector();
450 EthTypeCriterion ethType =
451 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
452 if ((ethType == null) ||
453 (ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
Pier Ventree0ae7a32016-11-23 09:57:42 -0800454 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST) &&
455 (ethType.ethType().toShort() != Ethernet.TYPE_IPV6)) {
Saurav Das4ce45962015-11-24 23:21:05 -0800456 log.warn("processSpecific: Unsupported forwarding objective criteria"
457 + "ethType:{} in dev:{}", ethType, deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800458 fail(fwd, ObjectiveError.UNSUPPORTED);
459 return Collections.emptySet();
460 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800461 boolean defaultRule = false;
Saurav Das8a0732e2015-11-20 15:27:53 -0800462 int forTableId = -1;
463 TrafficSelector.Builder filteredSelector = DefaultTrafficSelector.builder();
Flavio Castroe10fa242016-01-15 12:43:51 -0800464 TrafficSelector.Builder complementarySelector = DefaultTrafficSelector.builder();
465
Saurav Das8a0732e2015-11-20 15:27:53 -0800466 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Flavio Castroe10fa242016-01-15 12:43:51 -0800467 IpPrefix ipv4Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV4_DST)).ip();
Charles Chan5b9df8d2016-03-28 22:21:40 -0700468 if (ipv4Dst.isMulticast()) {
469 if (ipv4Dst.prefixLength() != 32) {
470 log.warn("Multicast specific forwarding objective can only be /32");
471 fail(fwd, ObjectiveError.BADPARAMS);
472 return ImmutableSet.of();
473 }
474 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
475 if (assignedVlan == null) {
476 log.warn("VLAN ID required by multicast specific fwd obj is missing. Abort.");
477 fail(fwd, ObjectiveError.BADPARAMS);
478 return ImmutableSet.of();
479 }
480 filteredSelector.matchVlanId(assignedVlan);
481 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
482 forTableId = MULTICAST_ROUTING_TABLE;
483 log.debug("processing IPv4 multicast specific forwarding objective {} -> next:{}"
484 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800485 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700486 if (ipv4Dst.prefixLength() == 0) {
487 // The entire IPV4_DST field is wildcarded intentionally
488 filteredSelector.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700489 } else {
Charles Chanf9e98652016-09-07 16:54:23 -0700490 filteredSelector.matchEthType(Ethernet.TYPE_IPV4).matchIPDst(ipv4Dst);
Charles Chan5b9df8d2016-03-28 22:21:40 -0700491 }
492 forTableId = UNICAST_ROUTING_TABLE;
493 log.debug("processing IPv4 unicast specific forwarding objective {} -> next:{}"
494 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
Flavio Castroe10fa242016-01-15 12:43:51 -0800495 }
Pier Ventree0ae7a32016-11-23 09:57:42 -0800496 } else if (ethType.ethType().toShort() == Ethernet.TYPE_IPV6) {
Julia Fergusond8f145e2017-08-10 18:15:24 +0000497 IpPrefix ipv6Dst = ((IPCriterion) selector.getCriterion(Criterion.Type.IPV6_DST)).ip();
498 if (ipv6Dst.isMulticast()) {
499 if (ipv6Dst.prefixLength() != IpAddress.INET6_BIT_LENGTH) {
500 log.debug("Multicast specific IPv6 forwarding objective can only be /128");
501 fail(fwd, ObjectiveError.BADPARAMS);
502 return ImmutableSet.of();
503 }
504 VlanId assignedVlan = readVlanFromSelector(fwd.meta());
505 if (assignedVlan == null) {
506 log.debug("VLAN ID required by multicast specific fwd obj is missing. Abort.");
507 fail(fwd, ObjectiveError.BADPARAMS);
508 return ImmutableSet.of();
509 }
510 filteredSelector.matchVlanId(assignedVlan);
511 filteredSelector.matchEthType(Ethernet.TYPE_IPV6).matchIPv6Dst(ipv6Dst);
512 forTableId = MULTICAST_ROUTING_TABLE;
513 log.debug("processing IPv6 multicast specific forwarding objective {} -> next:{}"
514 + " in dev:{}", fwd.id(), fwd.nextId(), deviceId);
515 } else {
516 if (buildIpv6Selector(filteredSelector, fwd) < 0) {
517 return Collections.emptyList();
518 }
519 forTableId = UNICAST_ROUTING_TABLE;
Pier Ventree0ae7a32016-11-23 09:57:42 -0800520 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800521 } else {
522 filteredSelector
523 .matchEthType(Ethernet.MPLS_UNICAST)
524 .matchMplsLabel(((MplsCriterion)
525 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
526 MplsBosCriterion bos = (MplsBosCriterion) selector
527 .getCriterion(Criterion.Type.MPLS_BOS);
528 if (bos != null) {
529 filteredSelector.matchMplsBos(bos.mplsBos());
530 }
531 forTableId = MPLS_TABLE_1;
Saurav Das4ce45962015-11-24 23:21:05 -0800532 log.debug("processing MPLS specific forwarding objective {} -> next:{}"
533 + " in dev {}", fwd.id(), fwd.nextId(), deviceId);
Saurav Das8a0732e2015-11-20 15:27:53 -0800534 }
535
536 TrafficTreatment.Builder tb = DefaultTrafficTreatment.builder();
537 if (fwd.treatment() != null) {
538 for (Instruction i : fwd.treatment().allInstructions()) {
Charles Chan40132b32017-01-22 00:19:37 -0800539 if (!supportCopyTtl() && i instanceof L3ModificationInstruction) {
540 L3ModificationInstruction l3instr = (L3ModificationInstruction) i;
541 if (l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_IN) ||
542 l3instr.subtype().equals(L3ModificationInstruction.L3SubType.TTL_OUT)) {
543 continue;
544 }
545 }
Charles Chan7d10b162015-12-07 18:54:45 -0800546 /*
547 * NOTE: OF-DPA does not support immediate instruction in
548 * L3 unicast and MPLS table.
549 */
550 tb.deferred().add(i);
Saurav Das8a0732e2015-11-20 15:27:53 -0800551 }
552 }
553
554 if (fwd.nextId() != null) {
Saurav Das423fe2b2015-12-04 10:52:59 -0800555 NextGroup next = getGroupForNextObjective(fwd.nextId());
556 if (next != null) {
557 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
558 // we only need the top level group's key to point the flow to it
559 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
560 if (group == null) {
Saurav Das1ce0a7b2016-10-21 14:06:29 -0700561 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
562 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
Saurav Das423fe2b2015-12-04 10:52:59 -0800563 fail(fwd, ObjectiveError.GROUPMISSING);
564 return Collections.emptySet();
565 }
566 tb.deferred().group(group.id());
Saurav Das8a0732e2015-11-20 15:27:53 -0800567 }
Saurav Das8a0732e2015-11-20 15:27:53 -0800568 }
569 tb.transition(ACL_TABLE);
570 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
571 .fromApp(fwd.appId())
572 .withPriority(fwd.priority())
573 .forDevice(deviceId)
574 .withSelector(filteredSelector.build())
575 .withTreatment(tb.build())
576 .forTable(forTableId);
577
578 if (fwd.permanent()) {
579 ruleBuilder.makePermanent();
580 } else {
581 ruleBuilder.makeTemporary(fwd.timeout());
582 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800583 Collection<FlowRule> flowRuleCollection = new ArrayList<>();
584 flowRuleCollection.add(ruleBuilder.build());
585 if (defaultRule) {
Pier Ventree0ae7a32016-11-23 09:57:42 -0800586 flowRuleCollection.add(
587 defaultRoute(fwd, complementarySelector, forTableId, tb)
588 );
Flavio Castroe10fa242016-01-15 12:43:51 -0800589 log.debug("Default rule 0.0.0.0/0 is being installed two rules");
590 }
Flavio Castroe10fa242016-01-15 12:43:51 -0800591 return flowRuleCollection;
Saurav Das8a0732e2015-11-20 15:27:53 -0800592 }
593
Charles Chan1e492d32016-01-30 23:22:37 -0800594 @Override
595 protected Collection<FlowRule> processEthDstSpecific(ForwardingObjective fwd) {
596 List<FlowRule> rules = new ArrayList<>();
597
598 // Build filtered selector
599 TrafficSelector selector = fwd.selector();
600 EthCriterion ethCriterion = (EthCriterion) selector
601 .getCriterion(Criterion.Type.ETH_DST);
602 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
603 .getCriterion(Criterion.Type.VLAN_VID);
604
605 if (vlanIdCriterion == null) {
606 log.warn("Forwarding objective for bridging requires vlan. Not "
607 + "installing fwd:{} in dev:{}", fwd.id(), deviceId);
608 fail(fwd, ObjectiveError.BADPARAMS);
609 return Collections.emptySet();
610 }
611
612 TrafficSelector.Builder filteredSelectorBuilder =
613 DefaultTrafficSelector.builder();
614 // Do not match MacAddress for subnet broadcast entry
Charles Chand05f54b2017-02-13 11:56:54 -0800615 if (!ethCriterion.mac().equals(NONE) && !ethCriterion.mac().equals(BROADCAST)) {
Charles Chan1e492d32016-01-30 23:22:37 -0800616 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
617 log.debug("processing L2 forwarding objective:{} -> next:{} in dev:{}",
618 fwd.id(), fwd.nextId(), deviceId);
619 } else {
620 log.debug("processing L2 Broadcast forwarding objective:{} -> next:{} "
621 + "in dev:{} for vlan:{}",
622 fwd.id(), fwd.nextId(), deviceId, vlanIdCriterion.vlanId());
623 }
624 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
625 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
626
627 if (fwd.treatment() != null) {
628 log.warn("Ignoring traffic treatment in fwd rule {} meant for L2 table"
629 + "for dev:{}. Expecting only nextId", fwd.id(), deviceId);
630 }
631
632 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
633 if (fwd.nextId() != null) {
634 NextGroup next = getGroupForNextObjective(fwd.nextId());
635 if (next != null) {
636 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
637 // we only need the top level group's key to point the flow to it
638 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
639 if (group != null) {
640 treatmentBuilder.deferred().group(group.id());
641 } else {
642 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
643 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
644 fail(fwd, ObjectiveError.GROUPMISSING);
645 return Collections.emptySet();
646 }
647 }
648 }
649 treatmentBuilder.immediate().transition(ACL_TABLE);
650 TrafficTreatment filteredTreatment = treatmentBuilder.build();
651
652 // Build bridging table entries
653 FlowRule.Builder flowRuleBuilder = DefaultFlowRule.builder();
654 flowRuleBuilder.fromApp(fwd.appId())
655 .withPriority(fwd.priority())
656 .forDevice(deviceId)
657 .withSelector(filteredSelector)
658 .withTreatment(filteredTreatment)
659 .forTable(BRIDGING_TABLE);
660 if (fwd.permanent()) {
661 flowRuleBuilder.makePermanent();
662 } else {
663 flowRuleBuilder.makeTemporary(fwd.timeout());
664 }
665 rules.add(flowRuleBuilder.build());
666 return rules;
667 }
668
Saurav Das52025962016-01-28 22:30:01 -0800669 /*
670 * In the OF-DPA 2.0 pipeline, versatile forwarding objectives go to the
671 * ACL table. Because we pop off vlan tags in TMAC table,
672 * we need to avoid matching on vlans in the ACL table.
673 */
674 @Override
675 protected Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
676 log.info("Processing versatile forwarding objective");
677
678 EthTypeCriterion ethType =
679 (EthTypeCriterion) fwd.selector().getCriterion(Criterion.Type.ETH_TYPE);
680 if (ethType == null) {
681 log.error("Versatile forwarding objective must include ethType");
682 fail(fwd, ObjectiveError.BADPARAMS);
683 return Collections.emptySet();
684 }
685 if (fwd.nextId() == null && fwd.treatment() == null) {
686 log.error("Forwarding objective {} from {} must contain "
687 + "nextId or Treatment", fwd.selector(), fwd.appId());
688 return Collections.emptySet();
689 }
690
691 TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
692 fwd.selector().criteria().forEach(criterion -> {
693 if (criterion instanceof VlanIdCriterion) {
694 // avoid matching on vlans
695 return;
Pier Ventree0ae7a32016-11-23 09:57:42 -0800696 } else if (criterion instanceof Icmpv6TypeCriterion ||
697 criterion instanceof Icmpv6CodeCriterion) {
698 /*
699 * We silenty discard these criterions, our current
700 * OFDPA platform does not support these matches on
701 * the ACL table.
702 */
703 log.warn("ICMPv6 Type and ICMPv6 Code are not supported");
Saurav Das52025962016-01-28 22:30:01 -0800704 } else {
705 sbuilder.add(criterion);
706 }
707 });
708
709 // XXX driver does not currently do type checking as per Tables 65-67 in
710 // OFDPA 2.0 spec. The only allowed treatment is a punt to the controller.
711 TrafficTreatment.Builder ttBuilder = DefaultTrafficTreatment.builder();
712 if (fwd.treatment() != null) {
713 for (Instruction ins : fwd.treatment().allInstructions()) {
714 if (ins instanceof OutputInstruction) {
715 OutputInstruction o = (OutputInstruction) ins;
716 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan0f43e472017-02-14 14:00:16 -0800717 ttBuilder.transition(PUNT_TABLE);
Saurav Das52025962016-01-28 22:30:01 -0800718 } else {
719 log.warn("Only allowed treatments in versatile forwarding "
720 + "objectives are punts to the controller");
721 }
722 } else {
723 log.warn("Cannot process instruction in versatile fwd {}", ins);
724 }
725 }
Charles Chan2df0e8a2017-01-09 11:45:08 -0800726 if (fwd.treatment().clearedDeferred()) {
727 ttBuilder.wipeDeferred();
728 }
Saurav Das52025962016-01-28 22:30:01 -0800729 }
730 if (fwd.nextId() != null) {
731 // overide case
732 NextGroup next = getGroupForNextObjective(fwd.nextId());
733 List<Deque<GroupKey>> gkeys = appKryo.deserialize(next.data());
734 // we only need the top level group's key to point the flow to it
735 Group group = groupService.getGroup(deviceId, gkeys.get(0).peekFirst());
736 if (group == null) {
737 log.warn("Group with key:{} for next-id:{} not found in dev:{}",
738 gkeys.get(0).peekFirst(), fwd.nextId(), deviceId);
739 fail(fwd, ObjectiveError.GROUPMISSING);
740 return Collections.emptySet();
741 }
742 ttBuilder.deferred().group(group.id());
743 }
744
745 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
746 .fromApp(fwd.appId())
747 .withPriority(fwd.priority())
748 .forDevice(deviceId)
749 .withSelector(sbuilder.build())
750 .withTreatment(ttBuilder.build())
751 .makePermanent()
752 .forTable(ACL_TABLE);
753 return Collections.singletonList(ruleBuilder.build());
754 }
755
756 /*
757 * Cpqd emulation requires table-miss-entries in forwarding tables.
758 * Real OFDPA does not require these rules as they are put in by default.
759 *
760 * (non-Javadoc)
761 * @see org.onosproject.driver.pipeline.OFDPA2Pipeline#initializePipeline()
762 */
Saurav Das2857f382015-11-03 14:39:27 -0800763 @Override
Saurav Das558afec2015-05-31 17:12:48 -0700764 protected void initializePipeline() {
Charles Chanf6ec1532017-02-08 16:10:40 -0800765 initTableMiss(PORT_TABLE, VLAN_TABLE, null);
766 initTableMiss(VLAN_TABLE, ACL_TABLE, null);
767 initTableMiss(TMAC_TABLE, BRIDGING_TABLE, null);
768 initTableMiss(UNICAST_ROUTING_TABLE, ACL_TABLE, null);
769 initTableMiss(MULTICAST_ROUTING_TABLE, ACL_TABLE, null);
770 initTableMiss(MPLS_TABLE_0, MPLS_TABLE_1, null);
771 initTableMiss(MPLS_TABLE_1, ACL_TABLE, null);
772 initTableMiss(BRIDGING_TABLE, ACL_TABLE, null);
773 initTableMiss(ACL_TABLE, -1, null);
Charles Chan0f43e472017-02-14 14:00:16 -0800774
775 if (supportPuntGroup()) {
776 initTableMiss(PUNT_TABLE, -1,
777 DefaultTrafficTreatment.builder().punt().build());
778 initPopVlanPuntGroup();
779 } else {
780 initTableMiss(PUNT_TABLE, -1,
781 DefaultTrafficTreatment.builder().popVlan().punt().build());
782 }
Saurav Das558afec2015-05-31 17:12:48 -0700783 }
784
Charles Chanf6ec1532017-02-08 16:10:40 -0800785 /**
786 * Install table-miss flow entry.
787 *
788 * If treatment exists, use it directly.
789 * Else if treatment does not exist but nextTable > 0, transit to next table.
790 * Else apply empty treatment.
791 *
792 * @param thisTable this table ID
793 * @param nextTable next table ID
794 * @param treatment traffic treatment to apply.
795 */
796 private void initTableMiss(int thisTable, int nextTable, TrafficTreatment treatment) {
Saurav Das558afec2015-05-31 17:12:48 -0700797 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chanf6ec1532017-02-08 16:10:40 -0800798 TrafficSelector selector = DefaultTrafficSelector.builder().build();
Saurav Das558afec2015-05-31 17:12:48 -0700799
Charles Chanf6ec1532017-02-08 16:10:40 -0800800 if (treatment == null) {
801 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
802 if (nextTable > 0) {
803 tBuilder.transition(nextTable);
Saurav Das558afec2015-05-31 17:12:48 -0700804 }
Charles Chanf6ec1532017-02-08 16:10:40 -0800805 treatment = tBuilder.build();
806 }
Saurav Das558afec2015-05-31 17:12:48 -0700807
Charles Chanb7504392017-02-10 12:51:04 -0800808 FlowRule rule = DefaultFlowRule.builder()
809 .forDevice(deviceId)
Charles Chanf6ec1532017-02-08 16:10:40 -0800810 .withSelector(selector)
811 .withTreatment(treatment)
Charles Chanb7504392017-02-10 12:51:04 -0800812 .withPriority(LOWEST_PRIORITY)
813 .fromApp(driverId)
814 .makePermanent()
Charles Chanf6ec1532017-02-08 16:10:40 -0800815 .forTable(thisTable).build();
Charles Chanb7504392017-02-10 12:51:04 -0800816 ops = ops.add(rule);
Saurav Das2857f382015-11-03 14:39:27 -0800817
818 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
819 @Override
820 public void onSuccess(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -0800821 log.info("Initialized table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -0800822 }
Saurav Das2857f382015-11-03 14:39:27 -0800823 @Override
824 public void onError(FlowRuleOperations ops) {
Charles Chanf6ec1532017-02-08 16:10:40 -0800825 log.warn("Failed to initialize table {} on {}", thisTable, deviceId);
Saurav Das2857f382015-11-03 14:39:27 -0800826 }
827 }));
828 }
Charles Chan0f43e472017-02-14 14:00:16 -0800829
830 /**
831 * Builds a indirect group contains pop_vlan and punt actions.
832 * <p>
833 * Using group instead of immediate action to ensure that
834 * the copy of packet on the data plane is not affected by the pop vlan action.
835 */
836 private void initPopVlanPuntGroup() {
Yi Tsengef19de12017-04-24 11:33:05 -0700837 GroupKey groupKey = popVlanPuntGroupKey();
Charles Chan0f43e472017-02-14 14:00:16 -0800838 TrafficTreatment bucketTreatment = DefaultTrafficTreatment.builder()
839 .popVlan().punt().build();
840 GroupBucket bucket =
841 DefaultGroupBucket.createIndirectGroupBucket(bucketTreatment);
842 GroupDescription groupDesc =
843 new DefaultGroupDescription(
844 deviceId,
845 GroupDescription.Type.INDIRECT,
846 new GroupBuckets(Collections.singletonList(bucket)),
847 groupKey,
848 POP_VLAN_PUNT_GROUP_ID,
849 driverId);
850 groupService.addGroup(groupDesc);
851
852 log.info("Initialized pop vlan punt group on {}", deviceId);
853 }
Yi Tsengef19de12017-04-24 11:33:05 -0700854
855 /**
856 * Generates group key for a static indirect group that pop vlan and punt to
857 * controller.
858 *
859 * @return the group key of the indirect table
860 */
861 private GroupKey popVlanPuntGroupKey() {
862 int hash = POP_VLAN_PUNT_GROUP_ID | (Objects.hash(deviceId) & FOUR_BIT_MASK);
863 return new DefaultGroupKey(Ofdpa2Pipeline.appKryo.serialize(hash));
864 }
Saurav Das558afec2015-05-31 17:12:48 -0700865}