blob: b5541065b3f0b55292dd0e60abd96ba5524ab6d8 [file] [log] [blame]
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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
18import static org.onlab.util.Tools.groupedThreads;
19import static org.slf4j.LoggerFactory.getLogger;
20
21import com.google.common.cache.Cache;
22import com.google.common.cache.CacheBuilder;
23import com.google.common.cache.RemovalCause;
24import com.google.common.cache.RemovalNotification;
25
26import org.onlab.osgi.ServiceDirectory;
27import org.onlab.packet.Ethernet;
Charles Chan68aa62d2015-11-09 16:37:23 -080028import org.onlab.packet.MacAddress;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070029import org.onlab.packet.VlanId;
30import org.onlab.util.KryoNamespace;
31import org.onosproject.core.ApplicationId;
32import org.onosproject.core.CoreService;
33import org.onosproject.net.DeviceId;
34import org.onosproject.net.PortNumber;
35import org.onosproject.net.behaviour.NextGroup;
36import org.onosproject.net.behaviour.Pipeliner;
37import org.onosproject.net.behaviour.PipelinerContext;
38import org.onosproject.net.driver.AbstractHandlerBehaviour;
39import org.onosproject.net.flow.DefaultFlowRule;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.FlowRule;
43import org.onosproject.net.flow.FlowRuleOperations;
44import org.onosproject.net.flow.FlowRuleOperationsContext;
45import org.onosproject.net.flow.FlowRuleService;
46import org.onosproject.net.flow.TrafficSelector;
47import org.onosproject.net.flow.TrafficTreatment;
48import org.onosproject.net.flow.criteria.Criteria;
49import org.onosproject.net.flow.criteria.Criterion;
50import org.onosproject.net.flow.criteria.EthCriterion;
51import org.onosproject.net.flow.criteria.EthTypeCriterion;
52import org.onosproject.net.flow.criteria.IPCriterion;
53import org.onosproject.net.flow.criteria.MplsCriterion;
54import org.onosproject.net.flow.criteria.PortCriterion;
55import org.onosproject.net.flow.criteria.VlanIdCriterion;
56import org.onosproject.net.flow.instructions.Instruction;
Saurav Das822c4e22015-10-23 10:51:11 -070057import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Charles Chan68aa62d2015-11-09 16:37:23 -080058import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -070059import org.onosproject.net.flowobjective.FilteringObjective;
60import org.onosproject.net.flowobjective.FlowObjectiveStore;
61import org.onosproject.net.flowobjective.ForwardingObjective;
62import org.onosproject.net.flowobjective.NextObjective;
63import org.onosproject.net.flowobjective.Objective;
64import org.onosproject.net.flowobjective.ObjectiveError;
65import org.onosproject.net.group.DefaultGroupBucket;
66import org.onosproject.net.group.DefaultGroupDescription;
67import org.onosproject.net.group.DefaultGroupKey;
68import org.onosproject.net.group.Group;
69import org.onosproject.net.group.GroupBucket;
70import org.onosproject.net.group.GroupBuckets;
71import org.onosproject.net.group.GroupDescription;
72import org.onosproject.net.group.GroupEvent;
73import org.onosproject.net.group.GroupKey;
74import org.onosproject.net.group.GroupListener;
75import org.onosproject.net.group.GroupService;
76import org.slf4j.Logger;
77
78import java.util.ArrayList;
79import java.util.Collection;
80import java.util.Collections;
81import java.util.List;
82import java.util.Set;
83import java.util.concurrent.Executors;
84import java.util.concurrent.ScheduledExecutorService;
85import java.util.concurrent.TimeUnit;
86import java.util.stream.Collectors;
87
88/**
89 * Driver for SPRING-OPEN pipeline.
90 */
91public class SpringOpenTTP extends AbstractHandlerBehaviour
92 implements Pipeliner {
93
94 // Default table ID - compatible with CpqD switch
95 private static final int TABLE_VLAN = 0;
96 private static final int TABLE_TMAC = 1;
97 private static final int TABLE_IPV4_UNICAST = 2;
98 private static final int TABLE_MPLS = 3;
Charles Chan68aa62d2015-11-09 16:37:23 -080099 private static final int TABLE_DMAC = 4;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700100 private static final int TABLE_ACL = 5;
Charles Chan68aa62d2015-11-09 16:37:23 -0800101 private static final int TABLE_SMAC = 6;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700102
103 /**
104 * Set the default values. These variables will get overwritten based on the
105 * switch vendor type
106 */
107 protected int vlanTableId = TABLE_VLAN;
108 protected int tmacTableId = TABLE_TMAC;
109 protected int ipv4UnicastTableId = TABLE_IPV4_UNICAST;
110 protected int mplsTableId = TABLE_MPLS;
Charles Chan68aa62d2015-11-09 16:37:23 -0800111 protected int dstMacTableId = TABLE_DMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700112 protected int aclTableId = TABLE_ACL;
Charles Chan68aa62d2015-11-09 16:37:23 -0800113 protected int srcMacTableId = TABLE_SMAC;
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700114
115 protected final Logger log = getLogger(getClass());
116
117 private ServiceDirectory serviceDirectory;
118 private FlowRuleService flowRuleService;
119 private CoreService coreService;
120 protected GroupService groupService;
121 protected FlowObjectiveStore flowObjectiveStore;
122 protected DeviceId deviceId;
123 private ApplicationId appId;
124
125 private Cache<GroupKey, NextObjective> pendingGroups;
126
127 private ScheduledExecutorService groupChecker = Executors
128 .newScheduledThreadPool(2,
129 groupedThreads("onos/pipeliner",
130 "spring-open-%d"));
131 protected KryoNamespace appKryo = new KryoNamespace.Builder()
132 .register(GroupKey.class).register(DefaultGroupKey.class)
133 .register(SegmentRoutingGroup.class).register(byte[].class).build();
134
135 @Override
136 public void init(DeviceId deviceId, PipelinerContext context) {
137 this.serviceDirectory = context.directory();
138 this.deviceId = deviceId;
139
140 pendingGroups = CacheBuilder
141 .newBuilder()
142 .expireAfterWrite(20, TimeUnit.SECONDS)
143 .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
144 if (notification.getCause() == RemovalCause.EXPIRED) {
145 fail(notification.getValue(),
146 ObjectiveError.GROUPINSTALLATIONFAILED);
147 }
148 }).build();
149
150 groupChecker.scheduleAtFixedRate(new GroupChecker(), 0, 500,
151 TimeUnit.MILLISECONDS);
152
153 coreService = serviceDirectory.get(CoreService.class);
154 flowRuleService = serviceDirectory.get(FlowRuleService.class);
155 groupService = serviceDirectory.get(GroupService.class);
156 flowObjectiveStore = context.store();
157
158 groupService.addListener(new InnerGroupListener());
159
160 appId = coreService
161 .registerApplication("org.onosproject.driver.SpringOpenTTP");
162
163 setTableMissEntries();
164 log.info("Spring Open TTP driver initialized");
165 }
166
167 @Override
168 public void filter(FilteringObjective filteringObjective) {
169 if (filteringObjective.type() == FilteringObjective.Type.PERMIT) {
170 log.debug("processing PERMIT filter objective");
171 processFilter(filteringObjective,
172 filteringObjective.op() == Objective.Operation.ADD,
173 filteringObjective.appId());
174 } else {
175 log.debug("filter objective other than PERMIT 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 flowBuilder = FlowRuleOperations.builder();
184
185 rules = processForward(fwd);
186 switch (fwd.op()) {
187 case ADD:
188 rules.stream().filter(rule -> rule != null)
189 .forEach(flowBuilder::add);
190 break;
191 case REMOVE:
192 rules.stream().filter(rule -> rule != null)
193 .forEach(flowBuilder::remove);
194 break;
195 default:
196 fail(fwd, ObjectiveError.UNKNOWN);
197 log.warn("Unknown forwarding type {}", fwd.op());
198 }
199
200 flowRuleService.apply(flowBuilder
201 .build(new FlowRuleOperationsContext() {
202 @Override
203 public void onSuccess(FlowRuleOperations ops) {
204 pass(fwd);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700205 log.debug("Provisioned tables in {} with "
206 + "forwarding rules for segment "
207 + "router", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700208 }
209
210 @Override
211 public void onError(FlowRuleOperations ops) {
212 fail(fwd, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700213 log.warn("Failed to provision tables in {} with "
214 + "forwarding rules for segment router",
215 deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700216 }
217 }));
218
219 }
220
221 @Override
222 public void next(NextObjective nextObjective) {
sangho834e4b02015-05-01 09:38:25 -0700223
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700224 log.debug("Processing NextObjective id{} op{}", nextObjective.id(),
225 nextObjective.op());
sangho834e4b02015-05-01 09:38:25 -0700226 if (nextObjective.op() == Objective.Operation.REMOVE) {
sangho1e575652015-05-14 00:39:53 -0700227 if (nextObjective.next().isEmpty()) {
sangho834e4b02015-05-01 09:38:25 -0700228 removeGroup(nextObjective);
229 } else {
230 removeBucketFromGroup(nextObjective);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700231 }
sangho834e4b02015-05-01 09:38:25 -0700232 } else if (nextObjective.op() == Objective.Operation.ADD) {
233 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
234 if (nextGroup != null) {
235 addBucketToGroup(nextObjective);
236 } else {
237 addGroup(nextObjective);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700238 }
sangho834e4b02015-05-01 09:38:25 -0700239 } else {
240 log.warn("Unsupported operation {}", nextObjective.op());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700241 }
242
243 }
244
sangho834e4b02015-05-01 09:38:25 -0700245 private void removeGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700246 log.debug("removeGroup in {}: for next objective id {}",
247 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700248 final GroupKey key = new DefaultGroupKey(
249 appKryo.serialize(nextObjective.id()));
250 groupService.removeGroup(deviceId, key, appId);
251 }
252
253 private void addGroup(NextObjective nextObjective) {
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700254 log.debug("addGroup with type{} for nextObjective id {}",
255 nextObjective.type(), nextObjective.id());
Charles Chanc42e84e2015-10-20 16:24:19 -0700256 List<GroupBucket> buckets;
sangho834e4b02015-05-01 09:38:25 -0700257 switch (nextObjective.type()) {
258 case SIMPLE:
259 log.debug("processing SIMPLE next objective");
260 Collection<TrafficTreatment> treatments = nextObjective.next();
261 if (treatments.size() == 1) {
262 TrafficTreatment treatment = treatments.iterator().next();
263 GroupBucket bucket = DefaultGroupBucket
264 .createIndirectGroupBucket(treatment);
265 final GroupKey key = new DefaultGroupKey(
266 appKryo.serialize(nextObjective
267 .id()));
268 GroupDescription groupDescription = new DefaultGroupDescription(
269 deviceId,
270 GroupDescription.Type.INDIRECT,
271 new GroupBuckets(
272 Collections.singletonList(bucket)),
273 key,
Saurav Das100e3b82015-04-30 11:12:10 -0700274 null,
sangho834e4b02015-05-01 09:38:25 -0700275 nextObjective.appId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700276 log.debug("Creating SIMPLE group for next objective id {}",
277 nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700278 groupService.addGroup(groupDescription);
279 pendingGroups.put(key, nextObjective);
280 }
281 break;
282 case HASHED:
283 log.debug("processing HASHED next objective");
Charles Chanc42e84e2015-10-20 16:24:19 -0700284 buckets = nextObjective
sangho834e4b02015-05-01 09:38:25 -0700285 .next()
286 .stream()
287 .map((treatment) -> DefaultGroupBucket
288 .createSelectGroupBucket(treatment))
289 .collect(Collectors.toList());
290 if (!buckets.isEmpty()) {
291 final GroupKey key = new DefaultGroupKey(
292 appKryo.serialize(nextObjective
293 .id()));
294 GroupDescription groupDescription = new DefaultGroupDescription(
295 deviceId,
296 GroupDescription.Type.SELECT,
297 new GroupBuckets(buckets),
298 key,
Saurav Das100e3b82015-04-30 11:12:10 -0700299 null,
sangho834e4b02015-05-01 09:38:25 -0700300 nextObjective.appId());
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700301 log.debug("Creating HASHED group for next objective id {}",
302 nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700303 groupService.addGroup(groupDescription);
304 pendingGroups.put(key, nextObjective);
305 }
306 break;
307 case BROADCAST:
Charles Chanc42e84e2015-10-20 16:24:19 -0700308 log.debug("processing BROADCAST next objective");
309 buckets = nextObjective
310 .next()
311 .stream()
312 .map((treatment) -> DefaultGroupBucket
313 .createAllGroupBucket(treatment))
314 .collect(Collectors.toList());
315 if (!buckets.isEmpty()) {
316 final GroupKey key = new DefaultGroupKey(
317 appKryo.serialize(nextObjective
318 .id()));
319 GroupDescription groupDescription = new DefaultGroupDescription(
320 deviceId,
321 GroupDescription.Type.ALL,
322 new GroupBuckets(buckets),
323 key,
324 null,
325 nextObjective.appId());
326 log.debug("Creating BROADCAST group for next objective id {}",
327 nextObjective.id());
328 groupService.addGroup(groupDescription);
329 pendingGroups.put(key, nextObjective);
330 }
331 break;
sangho834e4b02015-05-01 09:38:25 -0700332 case FAILOVER:
Charles Chanc42e84e2015-10-20 16:24:19 -0700333 log.debug("FAILOVER next objectives not supported");
sangho834e4b02015-05-01 09:38:25 -0700334 fail(nextObjective, ObjectiveError.UNSUPPORTED);
335 log.warn("Unsupported next objective type {}", nextObjective.type());
336 break;
337 default:
338 fail(nextObjective, ObjectiveError.UNKNOWN);
339 log.warn("Unknown next objective type {}", nextObjective.type());
340 }
341 }
342
343 private void addBucketToGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700344 log.debug("addBucketToGroup in {}: for next objective id {}",
345 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700346 Collection<TrafficTreatment> treatments = nextObjective.next();
347 TrafficTreatment treatment = treatments.iterator().next();
348 final GroupKey key = new DefaultGroupKey(
349 appKryo.serialize(nextObjective
350 .id()));
351 Group group = groupService.getGroup(deviceId, key);
352 if (group == null) {
353 log.warn("Group is not found in {} for {}", deviceId, key);
354 return;
355 }
356 GroupBucket bucket;
357 if (group.type() == GroupDescription.Type.INDIRECT) {
358 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
359 } else if (group.type() == GroupDescription.Type.SELECT) {
360 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700361 } else if (group.type() == GroupDescription.Type.ALL) {
362 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700363 } else {
364 log.warn("Unsupported Group type {}", group.type());
365 return;
366 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700367 GroupBuckets bucketsToAdd = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700368 log.debug("Adding buckets to group id {} of next objective id {} in device {}",
369 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700370 groupService.addBucketsToGroup(deviceId, key, bucketsToAdd, key, appId);
371 }
372
373 private void removeBucketFromGroup(NextObjective nextObjective) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700374 log.debug("removeBucketFromGroup in {}: for next objective id {}",
375 deviceId, nextObjective.id());
sangho834e4b02015-05-01 09:38:25 -0700376 NextGroup nextGroup = flowObjectiveStore.getNextGroup(nextObjective.id());
377 if (nextGroup != null) {
378 Collection<TrafficTreatment> treatments = nextObjective.next();
379 TrafficTreatment treatment = treatments.iterator().next();
380 final GroupKey key = new DefaultGroupKey(
381 appKryo.serialize(nextObjective
382 .id()));
383 Group group = groupService.getGroup(deviceId, key);
384 if (group == null) {
385 log.warn("Group is not found in {} for {}", deviceId, key);
386 return;
387 }
388 GroupBucket bucket;
389 if (group.type() == GroupDescription.Type.INDIRECT) {
390 bucket = DefaultGroupBucket.createIndirectGroupBucket(treatment);
391 } else if (group.type() == GroupDescription.Type.SELECT) {
392 bucket = DefaultGroupBucket.createSelectGroupBucket(treatment);
Charles Chanc42e84e2015-10-20 16:24:19 -0700393 } else if (group.type() == GroupDescription.Type.ALL) {
394 bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
sangho834e4b02015-05-01 09:38:25 -0700395 } else {
396 log.warn("Unsupported Group type {}", group.type());
397 return;
398 }
Sho SHIMIZU98ffca82015-05-11 08:39:24 -0700399 GroupBuckets removeBuckets = new GroupBuckets(Collections.singletonList(bucket));
Srikanth Vavilapalli5428b6c2015-05-14 20:22:47 -0700400 log.debug("Removing buckets from group id {} of next objective id {} in device {}",
401 group.id(), nextObjective.id(), deviceId);
sangho834e4b02015-05-01 09:38:25 -0700402 groupService.removeBucketsFromGroup(deviceId, key, removeBuckets, key, appId);
403 }
404 }
405
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700406 private Collection<FlowRule> processForward(ForwardingObjective fwd) {
407 switch (fwd.flag()) {
408 case SPECIFIC:
409 return processSpecific(fwd);
410 case VERSATILE:
411 return processVersatile(fwd);
412 default:
413 fail(fwd, ObjectiveError.UNKNOWN);
414 log.warn("Unknown forwarding flag {}", fwd.flag());
415 }
416 return Collections.emptySet();
417 }
418
419 private Collection<FlowRule> processVersatile(ForwardingObjective fwd) {
sangho1e575652015-05-14 00:39:53 -0700420 log.debug("Processing versatile forwarding objective");
421 TrafficSelector selector = fwd.selector();
Saurav Das822c4e22015-10-23 10:51:11 -0700422 TrafficTreatment treatment = null;
sangho1e575652015-05-14 00:39:53 -0700423 EthTypeCriterion ethType =
424 (EthTypeCriterion) selector.getCriterion(Criterion.Type.ETH_TYPE);
425 if (ethType == null) {
426 log.error("Versatile forwarding objective must include ethType");
427 fail(fwd, ObjectiveError.UNKNOWN);
428 return Collections.emptySet();
429 }
430
sangho1e575652015-05-14 00:39:53 -0700431 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
432 .builder();
433 treatmentBuilder.wipeDeferred();
434
435 if (fwd.nextId() != null) {
436 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
437
438 if (next != null) {
439 GroupKey key = appKryo.deserialize(next.data());
440
441 Group group = groupService.getGroup(deviceId, key);
442
443 if (group == null) {
444 log.warn("The group left!");
445 fail(fwd, ObjectiveError.GROUPMISSING);
446 return Collections.emptySet();
447 }
448 treatmentBuilder.deferred().group(group.id());
Saurav Das822c4e22015-10-23 10:51:11 -0700449 treatment = treatmentBuilder.build();
sangho1e575652015-05-14 00:39:53 -0700450 log.debug("Adding OUTGROUP action");
451 }
Saurav Das822c4e22015-10-23 10:51:11 -0700452 } else if (fwd.treatment() != null) {
453 if (fwd.treatment().allInstructions().size() == 1 &&
454 fwd.treatment().allInstructions().get(0).type() == Instruction.Type.OUTPUT) {
455 OutputInstruction o = (OutputInstruction) fwd.treatment().allInstructions().get(0);
456 if (o.port() == PortNumber.CONTROLLER) {
Charles Chan68aa62d2015-11-09 16:37:23 -0800457 treatmentBuilder.punt();
458 treatment = treatmentBuilder.build();
459 } else {
460 treatment = fwd.treatment();
Saurav Das822c4e22015-10-23 10:51:11 -0700461 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800462 } else {
463 treatment = fwd.treatment();
Saurav Das822c4e22015-10-23 10:51:11 -0700464 }
sangho1e575652015-05-14 00:39:53 -0700465 } else {
Saurav Das822c4e22015-10-23 10:51:11 -0700466 log.warn("VERSATILE forwarding objective needs next objective ID "
467 + "or treatment.");
sangho1e575652015-05-14 00:39:53 -0700468 return Collections.emptySet();
469 }
470
sangho1e575652015-05-14 00:39:53 -0700471 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
472 .fromApp(fwd.appId()).withPriority(fwd.priority())
sanghof9d0bf12015-05-19 11:57:42 -0700473 .forDevice(deviceId).withSelector(fwd.selector())
sangho1e575652015-05-14 00:39:53 -0700474 .withTreatment(treatment);
475
476 if (fwd.permanent()) {
477 ruleBuilder.makePermanent();
478 } else {
479 ruleBuilder.makeTemporary(fwd.timeout());
480 }
481
482 ruleBuilder.forTable(aclTableId);
483 return Collections.singletonList(ruleBuilder.build());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700484 }
485
Charles Chan68aa62d2015-11-09 16:37:23 -0800486 private boolean isSupportedEthTypeObjective(ForwardingObjective fwd) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700487 TrafficSelector selector = fwd.selector();
488 EthTypeCriterion ethType = (EthTypeCriterion) selector
489 .getCriterion(Criterion.Type.ETH_TYPE);
490 if ((ethType == null) ||
Charles Chan68aa62d2015-11-09 16:37:23 -0800491 ((ethType.ethType().toShort() != Ethernet.TYPE_IPV4) &&
492 (ethType.ethType().toShort() != Ethernet.MPLS_UNICAST))) {
493 return false;
494 }
495 return true;
496 }
497
498 private boolean isSupportedEthDstObjective(ForwardingObjective fwd) {
499 TrafficSelector selector = fwd.selector();
500 EthCriterion ethDst = (EthCriterion) selector
501 .getCriterion(Criterion.Type.ETH_DST);
502 VlanIdCriterion vlanId = (VlanIdCriterion) selector
503 .getCriterion(Criterion.Type.VLAN_VID);
504 if (ethDst == null && vlanId == null) {
505 return false;
506 }
507 return true;
508 }
509
510 protected Collection<FlowRule> processSpecific(ForwardingObjective fwd) {
511 log.debug("Processing specific");
512 boolean isEthTypeObj = isSupportedEthTypeObjective(fwd);
513 boolean isEthDstObj = isSupportedEthDstObjective(fwd);
514
515 if (isEthTypeObj) {
516 return processEthTypeSpecificObjective(fwd);
517 } else if (isEthDstObj) {
518 return processEthDstSpecificObjective(fwd);
519 } else {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700520 log.warn("processSpecific: Unsupported "
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700521 + "forwarding objective criteraia");
522 fail(fwd, ObjectiveError.UNSUPPORTED);
523 return Collections.emptySet();
524 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800525 }
526
527 protected Collection<FlowRule>
528 processEthTypeSpecificObjective(ForwardingObjective fwd) {
529 TrafficSelector selector = fwd.selector();
530 EthTypeCriterion ethType = (EthTypeCriterion) selector
531 .getCriterion(Criterion.Type.ETH_TYPE);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700532
533 TrafficSelector.Builder filteredSelectorBuilder =
534 DefaultTrafficSelector.builder();
535 int forTableId = -1;
alshabibcaf1ca22015-06-25 15:18:16 -0700536 if (ethType.ethType().toShort() == Ethernet.TYPE_IPV4) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700537 filteredSelectorBuilder = filteredSelectorBuilder
538 .matchEthType(Ethernet.TYPE_IPV4)
539 .matchIPDst(((IPCriterion) selector
540 .getCriterion(Criterion.Type.IPV4_DST))
541 .ip());
542 forTableId = ipv4UnicastTableId;
543 log.debug("processing IPv4 specific forwarding objective");
544 } else {
545 filteredSelectorBuilder = filteredSelectorBuilder
546 .matchEthType(Ethernet.MPLS_UNICAST)
547 .matchMplsLabel(((MplsCriterion)
548 selector.getCriterion(Criterion.Type.MPLS_LABEL)).label());
549 //TODO: Add Match for BoS
550 //if (selector.getCriterion(Criterion.Type.MPLS_BOS) != null) {
551 //}
552 forTableId = mplsTableId;
553 log.debug("processing MPLS specific forwarding objective");
554 }
555
556 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
557 .builder();
558 if (fwd.treatment() != null) {
559 for (Instruction i : fwd.treatment().allInstructions()) {
560 treatmentBuilder.add(i);
561 }
562 }
563
564 //TODO: Analyze the forwarding objective here to make
565 //device specific decision such as no ECMP groups in Dell
566 //switches.
567 if (fwd.nextId() != null) {
568 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
569
570 if (next != null) {
571 GroupKey key = appKryo.deserialize(next.data());
572
573 Group group = groupService.getGroup(deviceId, key);
574
575 if (group == null) {
576 log.warn("The group left!");
577 fail(fwd, ObjectiveError.GROUPMISSING);
578 return Collections.emptySet();
579 }
sangho1e575652015-05-14 00:39:53 -0700580 treatmentBuilder.deferred().group(group.id());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700581 log.debug("Adding OUTGROUP action");
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700582 } else {
583 log.warn("processSpecific: No associated next objective object");
584 fail(fwd, ObjectiveError.GROUPMISSING);
585 return Collections.emptySet();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700586 }
587 }
588
589 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
590 TrafficTreatment treatment = treatmentBuilder.transition(aclTableId)
591 .build();
592
593 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
594 .fromApp(fwd.appId()).withPriority(fwd.priority())
595 .forDevice(deviceId).withSelector(filteredSelector)
596 .withTreatment(treatment);
597
598 if (fwd.permanent()) {
599 ruleBuilder.makePermanent();
600 } else {
601 ruleBuilder.makeTemporary(fwd.timeout());
602 }
603
604 ruleBuilder.forTable(forTableId);
605 return Collections.singletonList(ruleBuilder.build());
606
607 }
608
Charles Chan68aa62d2015-11-09 16:37:23 -0800609 protected Collection<FlowRule>
610 processEthDstSpecificObjective(ForwardingObjective fwd) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700611 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800612
613 // Build filtered selector
614 TrafficSelector selector = fwd.selector();
615 EthCriterion ethCriterion = (EthCriterion) selector
616 .getCriterion(Criterion.Type.ETH_DST);
617 VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) selector
618 .getCriterion(Criterion.Type.VLAN_VID);
619 TrafficSelector.Builder filteredSelectorBuilder =
620 DefaultTrafficSelector.builder();
621 // Do not match MacAddress for subnet broadcast entry
622 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
623 filteredSelectorBuilder.matchEthDst(ethCriterion.mac());
624 }
625 filteredSelectorBuilder.matchVlanId(vlanIdCriterion.vlanId());
626 TrafficSelector filteredSelector = filteredSelectorBuilder.build();
627
628 // Build filtered treatment
629 TrafficTreatment.Builder treatmentBuilder =
630 DefaultTrafficTreatment.builder();
631 if (fwd.treatment() != null) {
632 treatmentBuilder.deferred();
633 fwd.treatment().allInstructions().forEach(treatmentBuilder::add);
634 }
635 if (fwd.nextId() != null) {
636 NextGroup next = flowObjectiveStore.getNextGroup(fwd.nextId());
637 if (next != null) {
638 GroupKey key = appKryo.deserialize(next.data());
639 Group group = groupService.getGroup(deviceId, key);
640 if (group != null) {
641 treatmentBuilder.deferred().group(group.id());
642 } else {
643 log.warn("Group Missing");
644 fail(fwd, ObjectiveError.GROUPMISSING);
645 return Collections.emptySet();
646 }
647 }
648 }
649 treatmentBuilder.immediate().transition(aclTableId);
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(dstMacTableId);
660 if (fwd.permanent()) {
661 flowRuleBuilder.makePermanent();
662 } else {
663 flowRuleBuilder.makeTemporary(fwd.timeout());
664 }
665 rules.add(flowRuleBuilder.build());
666
667 /*
668 // TODO Emulate source MAC table behavior
669 // Do not install source MAC table entry for subnet broadcast
670 if (!ethCriterion.mac().equals(MacAddress.NONE)) {
671 // Build filtered selector
672 selector = fwd.selector();
673 ethCriterion = (EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST);
674 filteredSelectorBuilder = DefaultTrafficSelector.builder();
675 filteredSelectorBuilder.matchEthSrc(ethCriterion.mac());
676 filteredSelector = filteredSelectorBuilder.build();
677
678 // Build empty treatment. Apply existing instruction if match.
679 treatmentBuilder = DefaultTrafficTreatment.builder();
680 filteredTreatment = treatmentBuilder.build();
681
682 // Build bridging table entries
683 flowRuleBuilder = DefaultFlowRule.builder();
684 flowRuleBuilder.fromApp(fwd.appId())
685 .withPriority(fwd.priority())
686 .forDevice(deviceId)
687 .withSelector(filteredSelector)
688 .withTreatment(filteredTreatment)
689 .forTable(srcMacTableId)
690 .makePermanent();
691 rules.add(flowRuleBuilder.build());
692 }
693 */
694
695 return rules;
696 }
697
698 protected List<FlowRule> processEthDstFilter(EthCriterion ethCriterion,
699 VlanIdCriterion vlanIdCriterion,
700 FilteringObjective filt,
701 VlanId assignedVlan,
702 ApplicationId applicationId) {
703 //handling untagged packets via assigned VLAN
704 if (vlanIdCriterion.vlanId() == VlanId.NONE) {
705 vlanIdCriterion = (VlanIdCriterion) Criteria.matchVlanId(assignedVlan);
706 }
707
708 /*
709 * Note: CpqD switches do not handle MPLS-related operation properly
710 * for a packet with VLAN tag. We pop VLAN here as a workaround.
711 * Side effect: HostService learns redundant hosts with same MAC but
712 * different VLAN. No known side effect on the network reachability.
713 */
714 List<FlowRule> rules = new ArrayList<>();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700715 TrafficSelector.Builder selectorIp = DefaultTrafficSelector
716 .builder();
717 TrafficTreatment.Builder treatmentIp = DefaultTrafficTreatment
718 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800719 selectorIp.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700720 selectorIp.matchEthType(Ethernet.TYPE_IPV4);
Charles Chan68aa62d2015-11-09 16:37:23 -0800721 selectorIp.matchVlanId(vlanIdCriterion.vlanId());
722 treatmentIp.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700723 treatmentIp.transition(ipv4UnicastTableId);
724 FlowRule ruleIp = DefaultFlowRule.builder().forDevice(deviceId)
725 .withSelector(selectorIp.build())
726 .withTreatment(treatmentIp.build())
727 .withPriority(filt.priority()).fromApp(applicationId)
728 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800729 log.debug("adding IP ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700730 rules.add(ruleIp);
731
732 TrafficSelector.Builder selectorMpls = DefaultTrafficSelector
733 .builder();
734 TrafficTreatment.Builder treatmentMpls = DefaultTrafficTreatment
735 .builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800736 selectorMpls.matchEthDst(ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700737 selectorMpls.matchEthType(Ethernet.MPLS_UNICAST);
Charles Chan68aa62d2015-11-09 16:37:23 -0800738 selectorMpls.matchVlanId(vlanIdCriterion.vlanId());
739 treatmentMpls.popVlan();
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700740 treatmentMpls.transition(mplsTableId);
741 FlowRule ruleMpls = DefaultFlowRule.builder()
742 .forDevice(deviceId).withSelector(selectorMpls.build())
743 .withTreatment(treatmentMpls.build())
744 .withPriority(filt.priority()).fromApp(applicationId)
745 .makePermanent().forTable(tmacTableId).build();
Charles Chan68aa62d2015-11-09 16:37:23 -0800746 log.debug("adding MPLS ETH rule for MAC: {}", ethCriterion.mac());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700747 rules.add(ruleMpls);
748
749 return rules;
750 }
751
Charles Chan68aa62d2015-11-09 16:37:23 -0800752 protected List<FlowRule> processVlanIdFilter(VlanIdCriterion vlanIdCriterion,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700753 FilteringObjective filt,
Charles Chan68aa62d2015-11-09 16:37:23 -0800754 VlanId assignedVlan,
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700755 ApplicationId applicationId) {
Charles Chanc42e84e2015-10-20 16:24:19 -0700756 List<FlowRule> rules = new ArrayList<>();
Charles Chan68aa62d2015-11-09 16:37:23 -0800757 log.debug("adding rule for VLAN: {}", vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700758 TrafficSelector.Builder selector = DefaultTrafficSelector
759 .builder();
760 TrafficTreatment.Builder treatment = DefaultTrafficTreatment
761 .builder();
762 PortCriterion p = (PortCriterion) filt.key();
Charles Chan68aa62d2015-11-09 16:37:23 -0800763 if (vlanIdCriterion.vlanId() != VlanId.NONE) {
764 selector.matchVlanId(vlanIdCriterion.vlanId());
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700765 selector.matchInPort(p.port());
766 treatment.deferred().popVlan();
Charles Chan68aa62d2015-11-09 16:37:23 -0800767 } else {
768 selector.matchInPort(p.port());
769 treatment.immediate().pushVlan().setVlanId(assignedVlan);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700770 }
771 treatment.transition(tmacTableId);
772 FlowRule rule = DefaultFlowRule.builder().forDevice(deviceId)
773 .withSelector(selector.build())
774 .withTreatment(treatment.build())
775 .withPriority(filt.priority()).fromApp(applicationId)
776 .makePermanent().forTable(vlanTableId).build();
777 rules.add(rule);
778
779 return rules;
780 }
781
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700782 private void processFilter(FilteringObjective filt, boolean install,
783 ApplicationId applicationId) {
784 // This driver only processes filtering criteria defined with switch
785 // ports as the key
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700786 if (filt.key().equals(Criteria.dummy())
787 || filt.key().type() != Criterion.Type.IN_PORT) {
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700788 log.warn("No key defined in filtering objective from app: {}. Not"
789 + "processing filtering objective", applicationId);
790 fail(filt, ObjectiveError.UNKNOWN);
791 return;
792 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800793
794 EthCriterion ethCriterion = null;
795 VlanIdCriterion vlanIdCriterion = null;
796
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700797 // convert filtering conditions for switch-intfs into flowrules
798 FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
Charles Chan68aa62d2015-11-09 16:37:23 -0800799
800 for (Criterion criterion : filt.conditions()) {
801 if (criterion.type() == Criterion.Type.ETH_DST) {
802 ethCriterion = (EthCriterion) criterion;
803 } else if (criterion.type() == Criterion.Type.VLAN_VID) {
804 vlanIdCriterion = (VlanIdCriterion) criterion;
805 } else if (criterion.type() == Criterion.Type.IPV4_DST) {
Saurav Das822c4e22015-10-23 10:51:11 -0700806 log.debug("driver does not process IP filtering rules as it " +
807 "sends all misses in the IP table to the controller");
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700808 } else {
809 log.warn("Driver does not currently process filtering condition"
Charles Chan68aa62d2015-11-09 16:37:23 -0800810 + " of type: {}", criterion.type());
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700811 fail(filt, ObjectiveError.UNSUPPORTED);
812 }
813 }
Charles Chan68aa62d2015-11-09 16:37:23 -0800814
815 VlanId assignedVlan = null;
816 if (vlanIdCriterion != null && vlanIdCriterion.vlanId() == VlanId.NONE) {
817 // Assign a VLAN ID to untagged packets
818 if (filt.meta() == null) {
819 log.error("Missing metadata in filtering objective required "
820 + "for vlan assignment in dev {}", deviceId);
821 fail(filt, ObjectiveError.BADPARAMS);
822 return;
823 }
824 for (Instruction i : filt.meta().allInstructions()) {
825 if (i instanceof ModVlanIdInstruction) {
826 assignedVlan = ((ModVlanIdInstruction) i).vlanId();
827 }
828 }
829 if (assignedVlan == null) {
830 log.error("Driver requires an assigned vlan-id to tag incoming "
831 + "untagged packets. Not processing vlan filters on "
832 + "device {}", deviceId);
833 fail(filt, ObjectiveError.BADPARAMS);
834 return;
835 }
836 }
837
838 if (ethCriterion == null) {
839 log.debug("filtering objective missing dstMac, cannot program TMAC table");
840 } else {
841 for (FlowRule tmacRule : processEthDstFilter(ethCriterion,
842 vlanIdCriterion,
843 filt,
844 assignedVlan,
845 applicationId)) {
846 log.debug("adding MAC filtering rules in TMAC table: {} for dev: {}",
847 tmacRule, deviceId);
848 ops = install ? ops.add(tmacRule) : ops.remove(tmacRule);
849 }
850 }
851
852 if (ethCriterion == null || vlanIdCriterion == null) {
853 log.debug("filtering objective missing dstMac or vlan, cannot program"
854 + "Vlan Table");
855 } else {
856 for (FlowRule vlanRule : processVlanIdFilter(vlanIdCriterion,
857 filt,
858 assignedVlan,
859 applicationId)) {
860 log.debug("adding VLAN filtering rule in VLAN table: {} for dev: {}",
861 vlanRule, deviceId);
862 ops = install ? ops.add(vlanRule) : ops.remove(vlanRule);
863 }
864 }
865
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700866 // apply filtering flow rules
867 flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
868 @Override
869 public void onSuccess(FlowRuleOperations ops) {
870 pass(filt);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700871 log.debug("Provisioned tables in {} with fitering "
872 + "rules for segment router", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700873 }
874
875 @Override
876 public void onError(FlowRuleOperations ops) {
877 fail(filt, ObjectiveError.FLOWINSTALLATIONFAILED);
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700878 log.warn("Failed to provision tables in {} with "
879 + "fitering rules for segment router", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700880 }
881 }));
882 }
883
884 protected void setTableMissEntries() {
885 // set all table-miss-entries
886 populateTableMissEntry(vlanTableId, true, false, false, -1);
Charles Chan68aa62d2015-11-09 16:37:23 -0800887 populateTableMissEntry(tmacTableId, false, false, true, dstMacTableId);
888 populateTableMissEntry(ipv4UnicastTableId, false, true, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700889 populateTableMissEntry(mplsTableId, false, true, true, aclTableId);
Charles Chan68aa62d2015-11-09 16:37:23 -0800890 populateTableMissEntry(dstMacTableId, false, false, true, aclTableId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700891 populateTableMissEntry(aclTableId, false, false, false, -1);
892 }
893
894 protected void populateTableMissEntry(int tableToAdd,
895 boolean toControllerNow,
896 boolean toControllerWrite,
897 boolean toTable, int tableToSend) {
898 TrafficSelector selector = DefaultTrafficSelector.builder().build();
899 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
900
901 if (toControllerNow) {
902 tBuilder.setOutput(PortNumber.CONTROLLER);
903 }
904
905 if (toControllerWrite) {
906 tBuilder.deferred().setOutput(PortNumber.CONTROLLER);
907 }
908
909 if (toTable) {
910 tBuilder.transition(tableToSend);
911 }
912
913 FlowRule flow = DefaultFlowRule.builder().forDevice(deviceId)
914 .withSelector(selector).withTreatment(tBuilder.build())
915 .withPriority(0).fromApp(appId).makePermanent()
916 .forTable(tableToAdd).build();
917
918 flowRuleService.applyFlowRules(flow);
919 }
920
921 private void pass(Objective obj) {
922 if (obj.context().isPresent()) {
923 obj.context().get().onSuccess(obj);
924 }
925 }
926
927 protected void fail(Objective obj, ObjectiveError error) {
928 if (obj.context().isPresent()) {
929 obj.context().get().onError(obj, error);
930 }
931 }
932
933 private class InnerGroupListener implements GroupListener {
934 @Override
935 public void event(GroupEvent event) {
936 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700937 log.debug("InnerGroupListener: Group ADDED "
938 + "event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700939 GroupKey key = event.subject().appCookie();
940
941 NextObjective obj = pendingGroups.getIfPresent(key);
942 if (obj != null) {
943 flowObjectiveStore
944 .putNextGroup(obj.id(),
945 new SegmentRoutingGroup(key));
946 pass(obj);
947 pendingGroups.invalidate(key);
948 }
Srikanth Vavilapalli23181912015-05-04 09:48:09 -0700949 } else if (event.type() == GroupEvent.Type.GROUP_ADD_FAILED) {
950 log.warn("InnerGroupListener: Group ADD "
951 + "failed event received in device {}", deviceId);
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700952 }
953 }
954 }
955
956 private class GroupChecker implements Runnable {
957
958 @Override
959 public void run() {
960 Set<GroupKey> keys = pendingGroups
961 .asMap()
962 .keySet()
963 .stream()
964 .filter(key -> groupService.getGroup(deviceId, key) != null)
965 .collect(Collectors.toSet());
966
967 keys.stream()
968 .forEach(key -> {
969 NextObjective obj = pendingGroups
970 .getIfPresent(key);
971 if (obj == null) {
972 return;
973 }
974 pass(obj);
975 pendingGroups.invalidate(key);
976 flowObjectiveStore.putNextGroup(obj.id(),
977 new SegmentRoutingGroup(
978 key));
979 });
980 }
981 }
982
983 private class SegmentRoutingGroup implements NextGroup {
984
985 private final GroupKey key;
986
987 public SegmentRoutingGroup(GroupKey key) {
988 this.key = key;
989 }
990
Saurav Das822c4e22015-10-23 10:51:11 -0700991 @SuppressWarnings("unused")
Srikanth Vavilapallif5b234a2015-04-21 13:04:13 -0700992 public GroupKey key() {
993 return key;
994 }
995
996 @Override
997 public byte[] data() {
998 return appKryo.serialize(key);
999 }
1000
1001 }
1002}