blob: 1ce97824d0d536457cf29abacfc9457dd0c5cb98 [file] [log] [blame]
alshabib0ccde6d2015-05-30 18:22:36 -07001/*
Charles Chanaedabfd2016-02-26 09:31:48 -08002 * Copyright 2015-2016 Open Networking Laboratory
alshabib0ccde6d2015-05-30 18:22:36 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.driver.pipeline;
17
alshabibd61b77b2016-02-01 23:30:53 -080018import com.google.common.cache.Cache;
19import com.google.common.cache.CacheBuilder;
20import com.google.common.cache.RemovalCause;
21import com.google.common.cache.RemovalNotification;
alshabibfd430b62015-12-16 18:56:38 -080022import com.google.common.collect.Lists;
23import org.apache.commons.lang3.tuple.ImmutablePair;
24import org.apache.commons.lang3.tuple.Pair;
alshabib0ccde6d2015-05-30 18:22:36 -070025import org.onlab.osgi.ServiceDirectory;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070026import org.onlab.packet.EthType;
alshabibfd430b62015-12-16 18:56:38 -080027import org.onlab.packet.IPv4;
28import org.onlab.packet.VlanId;
alshabibd61b77b2016-02-01 23:30:53 -080029import org.onlab.util.KryoNamespace;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070030import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
alshabib0ccde6d2015-05-30 18:22:36 -070032import org.onosproject.net.DeviceId;
33import org.onosproject.net.PortNumber;
alshabibd61b77b2016-02-01 23:30:53 -080034import org.onosproject.net.behaviour.NextGroup;
alshabib0ccde6d2015-05-30 18:22:36 -070035import org.onosproject.net.behaviour.Pipeliner;
36import org.onosproject.net.behaviour.PipelinerContext;
37import org.onosproject.net.driver.AbstractHandlerBehaviour;
38import org.onosproject.net.flow.DefaultFlowRule;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070039import org.onosproject.net.flow.DefaultTrafficSelector;
alshabib0ccde6d2015-05-30 18:22:36 -070040import org.onosproject.net.flow.DefaultTrafficTreatment;
41import org.onosproject.net.flow.FlowRule;
42import org.onosproject.net.flow.FlowRuleOperations;
43import org.onosproject.net.flow.FlowRuleOperationsContext;
44import org.onosproject.net.flow.FlowRuleService;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
alshabibfd430b62015-12-16 18:56:38 -080047import org.onosproject.net.flow.criteria.Criteria;
48import org.onosproject.net.flow.criteria.Criterion;
49import org.onosproject.net.flow.criteria.EthTypeCriterion;
alshabib1aa58142016-02-17 15:37:56 -080050import org.onosproject.net.flow.criteria.IPCriterion;
alshabibfd430b62015-12-16 18:56:38 -080051import org.onosproject.net.flow.criteria.IPProtocolCriterion;
52import org.onosproject.net.flow.criteria.PortCriterion;
alshabib2f74f2c2016-01-14 13:29:35 -080053import org.onosproject.net.flow.criteria.VlanIdCriterion;
alshabibfd430b62015-12-16 18:56:38 -080054import org.onosproject.net.flow.instructions.Instruction;
alshabib0ccde6d2015-05-30 18:22:36 -070055import org.onosproject.net.flow.instructions.Instructions;
alshabibfd430b62015-12-16 18:56:38 -080056import org.onosproject.net.flow.instructions.L2ModificationInstruction;
alshabib0ccde6d2015-05-30 18:22:36 -070057import org.onosproject.net.flowobjective.FilteringObjective;
alshabibd61b77b2016-02-01 23:30:53 -080058import org.onosproject.net.flowobjective.FlowObjectiveStore;
alshabib0ccde6d2015-05-30 18:22:36 -070059import org.onosproject.net.flowobjective.ForwardingObjective;
60import org.onosproject.net.flowobjective.NextObjective;
alshabibfd430b62015-12-16 18:56:38 -080061import org.onosproject.net.flowobjective.Objective;
alshabib0ccde6d2015-05-30 18:22:36 -070062import org.onosproject.net.flowobjective.ObjectiveError;
alshabibd61b77b2016-02-01 23:30:53 -080063import org.onosproject.net.group.DefaultGroupBucket;
64import org.onosproject.net.group.DefaultGroupDescription;
65import org.onosproject.net.group.DefaultGroupKey;
66import org.onosproject.net.group.Group;
67import org.onosproject.net.group.GroupBucket;
68import org.onosproject.net.group.GroupBuckets;
69import org.onosproject.net.group.GroupDescription;
70import org.onosproject.net.group.GroupEvent;
71import org.onosproject.net.group.GroupKey;
72import org.onosproject.net.group.GroupListener;
73import org.onosproject.net.group.GroupService;
74import org.onosproject.store.serializers.KryoNamespaces;
alshabib5ccbe3f2016-03-02 22:36:02 -080075import org.onosproject.store.service.AtomicCounter;
76import org.onosproject.store.service.StorageService;
alshabib0ccde6d2015-05-30 18:22:36 -070077import org.slf4j.Logger;
78
alshabibfd430b62015-12-16 18:56:38 -080079import java.util.Collection;
alshabibd61b77b2016-02-01 23:30:53 -080080import java.util.Collections;
alshabibfd430b62015-12-16 18:56:38 -080081import java.util.List;
82import java.util.Optional;
alshabibd61b77b2016-02-01 23:30:53 -080083import java.util.concurrent.TimeUnit;
alshabibfd430b62015-12-16 18:56:38 -080084import java.util.stream.Collectors;
85
alshabib0ccde6d2015-05-30 18:22:36 -070086import static org.slf4j.LoggerFactory.getLogger;
87
88/**
Jonathan Hart64da69d2015-07-15 15:10:28 -070089 * Pipeliner for OLT device.
alshabib0ccde6d2015-05-30 18:22:36 -070090 */
alshabibfd430b62015-12-16 18:56:38 -080091
Jonathan Hartb92cc512015-11-16 23:05:21 -080092public class OltPipeline extends AbstractHandlerBehaviour implements Pipeliner {
alshabib0ccde6d2015-05-30 18:22:36 -070093
alshabibfd430b62015-12-16 18:56:38 -080094 private static final Integer QQ_TABLE = 1;
alshabibd61b77b2016-02-01 23:30:53 -080095 private static final short MCAST_VLAN = 4000;
alshabib5ccbe3f2016-03-02 22:36:02 -080096 private static final String OLTCOOKIES = "olt-cookies-must-be-unique";
alshabib0ccde6d2015-05-30 18:22:36 -070097 private final Logger log = getLogger(getClass());
98
99 private ServiceDirectory serviceDirectory;
100 private FlowRuleService flowRuleService;
alshabibd61b77b2016-02-01 23:30:53 -0800101 private GroupService groupService;
Jonathan Hartdfc3b862015-07-01 14:49:56 -0700102 private CoreService coreService;
alshabib5ccbe3f2016-03-02 22:36:02 -0800103 private StorageService storageService;
Jonathan Hartdfc3b862015-07-01 14:49:56 -0700104
alshabibd61b77b2016-02-01 23:30:53 -0800105 private DeviceId deviceId;
Jonathan Hartdfc3b862015-07-01 14:49:56 -0700106 private ApplicationId appId;
Charles Chanaedabfd2016-02-26 09:31:48 -0800107 // NOTE: OLT currently has some issue with cookie 0. Pick something larger
108 // to avoid collision
alshabib5ccbe3f2016-03-02 22:36:02 -0800109 private AtomicCounter counter;
alshabib0ccde6d2015-05-30 18:22:36 -0700110
alshabibd61b77b2016-02-01 23:30:53 -0800111 protected FlowObjectiveStore flowObjectiveStore;
112
113 private Cache<GroupKey, NextObjective> pendingGroups;
114
115 protected static KryoNamespace appKryo = new KryoNamespace.Builder()
116 .register(KryoNamespaces.API)
117 .register(GroupKey.class)
118 .register(DefaultGroupKey.class)
119 .register(OLTPipelineGroup.class)
120 .register(byte[].class)
121 .build();
alshabib2cc73cb2015-06-30 20:26:56 -0700122
alshabib0ccde6d2015-05-30 18:22:36 -0700123 @Override
124 public void init(DeviceId deviceId, PipelinerContext context) {
alshabibfd430b62015-12-16 18:56:38 -0800125 log.debug("Initiate OLT pipeline");
alshabib0ccde6d2015-05-30 18:22:36 -0700126 this.serviceDirectory = context.directory();
127 this.deviceId = deviceId;
alshabibd61b77b2016-02-01 23:30:53 -0800128
alshabib0ccde6d2015-05-30 18:22:36 -0700129 flowRuleService = serviceDirectory.get(FlowRuleService.class);
Jonathan Hartdfc3b862015-07-01 14:49:56 -0700130 coreService = serviceDirectory.get(CoreService.class);
alshabibd61b77b2016-02-01 23:30:53 -0800131 groupService = serviceDirectory.get(GroupService.class);
132 flowObjectiveStore = context.store();
alshabib5ccbe3f2016-03-02 22:36:02 -0800133 storageService = serviceDirectory.get(StorageService.class);
134
135 counter = storageService.atomicCounterBuilder()
136 .withName(String.format(OLTCOOKIES, deviceId))
137 .build()
138 .asAtomicCounter();
139
140 /*
141 magic olt number to make sure we don't collide with it's internal
142 processing
143 */
144 counter.set(123);
alshabibd61b77b2016-02-01 23:30:53 -0800145
alshabibb32cefe2015-06-08 18:15:05 -0700146
Jonathan Hartdfc3b862015-07-01 14:49:56 -0700147 appId = coreService.registerApplication(
148 "org.onosproject.driver.OLTPipeline");
149
alshabibd61b77b2016-02-01 23:30:53 -0800150
151 pendingGroups = CacheBuilder.newBuilder()
152 .expireAfterWrite(20, TimeUnit.SECONDS)
153 .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
154 if (notification.getCause() == RemovalCause.EXPIRED) {
155 fail(notification.getValue(), ObjectiveError.GROUPINSTALLATIONFAILED);
156 }
157 }).build();
158
159 groupService.addListener(new InnerGroupListener());
160
alshabibb32cefe2015-06-08 18:15:05 -0700161 }
162
alshabib0ccde6d2015-05-30 18:22:36 -0700163 @Override
164 public void filter(FilteringObjective filter) {
alshabibfd430b62015-12-16 18:56:38 -0800165 Instructions.OutputInstruction output;
alshabib0ccde6d2015-05-30 18:22:36 -0700166
alshabibfd430b62015-12-16 18:56:38 -0800167 if (filter.meta() != null && !filter.meta().immediate().isEmpty()) {
168 output = (Instructions.OutputInstruction) filter.meta().immediate().stream()
169 .filter(t -> t.type().equals(Instruction.Type.OUTPUT))
170 .limit(1)
171 .findFirst().get();
alshabib0ccde6d2015-05-30 18:22:36 -0700172
alshabibbb424232016-01-15 12:20:25 -0800173 if (output == null || !output.port().equals(PortNumber.CONTROLLER)) {
174 log.error("OLT can only filter packet to controller");
alshabibfd430b62015-12-16 18:56:38 -0800175 fail(filter, ObjectiveError.UNSUPPORTED);
176 return;
alshabib0ccde6d2015-05-30 18:22:36 -0700177 }
alshabibfd430b62015-12-16 18:56:38 -0800178 } else {
179 fail(filter, ObjectiveError.BADPARAMS);
alshabib0ccde6d2015-05-30 18:22:36 -0700180 return;
181 }
182
alshabibfd430b62015-12-16 18:56:38 -0800183 if (filter.key().type() != Criterion.Type.IN_PORT) {
184 fail(filter, ObjectiveError.BADPARAMS);
185 return;
186 }
187
188 EthTypeCriterion ethType = (EthTypeCriterion)
189 filterForCriterion(filter.conditions(), Criterion.Type.ETH_TYPE);
190
191 if (ethType == null) {
192 fail(filter, ObjectiveError.BADPARAMS);
193 return;
194 }
195
alshabibbb424232016-01-15 12:20:25 -0800196 if (ethType.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
alshabibfd430b62015-12-16 18:56:38 -0800197 provisionEapol(filter, ethType, output);
alshabibbb424232016-01-15 12:20:25 -0800198 } else if (ethType.ethType().equals(EthType.EtherType.IPV4.ethType())) {
alshabibfd430b62015-12-16 18:56:38 -0800199 IPProtocolCriterion ipProto = (IPProtocolCriterion)
200 filterForCriterion(filter.conditions(), Criterion.Type.IP_PROTO);
201 if (ipProto.protocol() == IPv4.PROTOCOL_IGMP) {
Jonathan Hart51539b82015-10-29 09:53:04 -0700202 provisionIgmp(filter, ethType, ipProto, output);
alshabibbb424232016-01-15 12:20:25 -0800203 } else {
204 log.error("OLT can only filter igmp");
205 fail(filter, ObjectiveError.UNSUPPORTED);
alshabibfd430b62015-12-16 18:56:38 -0800206 }
207 } else {
alshabibbb424232016-01-15 12:20:25 -0800208 log.error("OLT can only filter eapol and igmp");
alshabibfd430b62015-12-16 18:56:38 -0800209 fail(filter, ObjectiveError.UNSUPPORTED);
210 }
211
212 }
213
214
215 @Override
216 public void forward(ForwardingObjective fwd) {
alshabibd61b77b2016-02-01 23:30:53 -0800217
218 if (checkForMulticast(fwd)) {
219 processMulticastRule(fwd);
220 return;
221 }
222
alshabib0ccde6d2015-05-30 18:22:36 -0700223 TrafficTreatment treatment = fwd.treatment();
alshabib0ccde6d2015-05-30 18:22:36 -0700224
alshabibfd430b62015-12-16 18:56:38 -0800225 List<Instruction> instructions = treatment.allInstructions();
alshabib0ccde6d2015-05-30 18:22:36 -0700226
alshabibfd430b62015-12-16 18:56:38 -0800227 Optional<Instruction> vlanIntruction = instructions.stream()
228 .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
229 .filter(i -> ((L2ModificationInstruction) i).subtype() ==
230 L2ModificationInstruction.L2SubType.VLAN_PUSH ||
231 ((L2ModificationInstruction) i).subtype() ==
232 L2ModificationInstruction.L2SubType.VLAN_POP)
233 .findAny();
234
235 if (!vlanIntruction.isPresent()) {
236 fail(fwd, ObjectiveError.BADPARAMS);
237 return;
238 }
239
240 L2ModificationInstruction vlanIns =
241 (L2ModificationInstruction) vlanIntruction.get();
242
243 if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
244 installUpstreamRules(fwd);
245 } else if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP) {
246 installDownstreamRules(fwd);
alshabib0ccde6d2015-05-30 18:22:36 -0700247 } else {
alshabibfd430b62015-12-16 18:56:38 -0800248 log.error("Unknown OLT operation: {}", fwd);
249 fail(fwd, ObjectiveError.UNSUPPORTED);
250 return;
alshabib0ccde6d2015-05-30 18:22:36 -0700251 }
252
alshabibfd430b62015-12-16 18:56:38 -0800253 pass(fwd);
alshabib0ccde6d2015-05-30 18:22:36 -0700254
alshabib0ccde6d2015-05-30 18:22:36 -0700255 }
256
alshabibd61b77b2016-02-01 23:30:53 -0800257
alshabib0ccde6d2015-05-30 18:22:36 -0700258 @Override
259 public void next(NextObjective nextObjective) {
alshabibd61b77b2016-02-01 23:30:53 -0800260 if (nextObjective.type() != NextObjective.Type.BROADCAST) {
261 log.error("OLT only supports broadcast groups.");
262 fail(nextObjective, ObjectiveError.BADPARAMS);
263 }
264
265 if (nextObjective.next().size() != 1) {
266 log.error("OLT only supports singleton broadcast groups.");
267 fail(nextObjective, ObjectiveError.BADPARAMS);
268 }
269
270 TrafficTreatment treatment = nextObjective.next().stream().findFirst().get();
271
272
273 GroupBucket bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
274 GroupKey key = new DefaultGroupKey(appKryo.serialize(nextObjective.id()));
275
alshabib1aa58142016-02-17 15:37:56 -0800276
alshabibd61b77b2016-02-01 23:30:53 -0800277 pendingGroups.put(key, nextObjective);
278
279 switch (nextObjective.op()) {
280 case ADD:
alshabib1aa58142016-02-17 15:37:56 -0800281 GroupDescription groupDesc =
282 new DefaultGroupDescription(deviceId,
283 GroupDescription.Type.ALL,
284 new GroupBuckets(Collections.singletonList(bucket)),
285 key,
286 null,
287 nextObjective.appId());
alshabibd61b77b2016-02-01 23:30:53 -0800288 groupService.addGroup(groupDesc);
289 break;
290 case REMOVE:
291 groupService.removeGroup(deviceId, key, nextObjective.appId());
292 break;
293 case ADD_TO_EXISTING:
alshabib1aa58142016-02-17 15:37:56 -0800294 groupService.addBucketsToGroup(deviceId, key,
295 new GroupBuckets(Collections.singletonList(bucket)),
296 key, nextObjective.appId());
297 break;
alshabibd61b77b2016-02-01 23:30:53 -0800298 case REMOVE_FROM_EXISTING:
alshabib1aa58142016-02-17 15:37:56 -0800299 groupService.removeBucketsFromGroup(deviceId, key,
alshabib56efe432016-02-25 17:57:24 -0500300 new GroupBuckets(Collections.singletonList(bucket)),
301 key, nextObjective.appId());
alshabibd61b77b2016-02-01 23:30:53 -0800302 break;
303 default:
304 log.warn("Unknown next objective operation: {}", nextObjective.op());
305 }
306
307
308 }
309
310 private void processMulticastRule(ForwardingObjective fwd) {
311 if (fwd.nextId() == null) {
312 log.error("Multicast objective does not have a next id");
313 fail(fwd, ObjectiveError.BADPARAMS);
314 }
315
alshabib1aa58142016-02-17 15:37:56 -0800316 GroupKey key = getGroupForNextObjective(fwd.nextId());
alshabibd61b77b2016-02-01 23:30:53 -0800317
alshabib1aa58142016-02-17 15:37:56 -0800318 if (key == null) {
alshabibd61b77b2016-02-01 23:30:53 -0800319 log.error("Group for forwarding objective missing: {}", fwd);
320 fail(fwd, ObjectiveError.GROUPMISSING);
321 }
322
alshabib1aa58142016-02-17 15:37:56 -0800323 Group group = groupService.getGroup(deviceId, key);
alshabibd61b77b2016-02-01 23:30:53 -0800324 TrafficTreatment treatment =
325 buildTreatment(Instructions.createGroup(group.id()));
326
327 FlowRule rule = DefaultFlowRule.builder()
alshabib56efe432016-02-25 17:57:24 -0500328 .withCookie(counter.getAndIncrement())
alshabibd61b77b2016-02-01 23:30:53 -0800329 .forDevice(deviceId)
330 .forTable(0)
alshabibd61b77b2016-02-01 23:30:53 -0800331 .makePermanent()
332 .withPriority(fwd.priority())
333 .withSelector(fwd.selector())
334 .withTreatment(treatment)
335 .build();
336
337 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
338 switch (fwd.op()) {
339
340 case ADD:
341 builder.add(rule);
342 break;
343 case REMOVE:
344 builder.remove(rule);
345 break;
346 case ADD_TO_EXISTING:
347 case REMOVE_FROM_EXISTING:
348 break;
349 default:
350 log.warn("Unknown forwarding operation: {}", fwd.op());
351 }
352
353 applyFlowRules(builder, fwd);
354
355 }
356
357 private boolean checkForMulticast(ForwardingObjective fwd) {
358
alshabib1aa58142016-02-17 15:37:56 -0800359 IPCriterion ip = (IPCriterion) filterForCriterion(fwd.selector().criteria(),
alshabib56efe432016-02-25 17:57:24 -0500360 Criterion.Type.IPV4_DST);
alshabibd61b77b2016-02-01 23:30:53 -0800361
alshabib1aa58142016-02-17 15:37:56 -0800362 if (ip == null) {
363 return false;
364 }
365
Charles Chanaedabfd2016-02-26 09:31:48 -0800366 return ip.ip().isMulticast();
alshabibd61b77b2016-02-01 23:30:53 -0800367
368 }
369
alshabib1aa58142016-02-17 15:37:56 -0800370 private GroupKey getGroupForNextObjective(Integer nextId) {
alshabibd61b77b2016-02-01 23:30:53 -0800371 NextGroup next = flowObjectiveStore.getNextGroup(nextId);
alshabib1aa58142016-02-17 15:37:56 -0800372 return appKryo.deserialize(next.data());
alshabibd61b77b2016-02-01 23:30:53 -0800373
alshabib0ccde6d2015-05-30 18:22:36 -0700374 }
375
alshabibfd430b62015-12-16 18:56:38 -0800376 private void installDownstreamRules(ForwardingObjective fwd) {
377 List<Pair<Instruction, Instruction>> vlanOps =
378 vlanOps(fwd,
379 L2ModificationInstruction.L2SubType.VLAN_POP);
380
381 if (vlanOps == null) {
382 return;
383 }
384
385 Instruction output = fetchOutput(fwd, "downstream");
386
387 if (output == null) {
388 return;
389 }
390
391 Pair<Instruction, Instruction> popAndRewrite = vlanOps.remove(0);
392
alshabibfa0dc662016-01-13 11:23:53 -0800393 TrafficSelector selector = fwd.selector();
394
395 Criterion outerVlan = selector.getCriterion(Criterion.Type.VLAN_VID);
396 Criterion innerVlan = selector.getCriterion(Criterion.Type.INNER_VLAN_VID);
397 Criterion inport = selector.getCriterion(Criterion.Type.IN_PORT);
398
399 if (outerVlan == null || innerVlan == null || inport == null) {
400 log.error("Forwarding objective is underspecified: {}", fwd);
401 fail(fwd, ObjectiveError.BADPARAMS);
402 return;
403 }
404
alshabib2f74f2c2016-01-14 13:29:35 -0800405 Criterion innerVid = Criteria.matchVlanId(((VlanIdCriterion) innerVlan).vlanId());
406
alshabibfa0dc662016-01-13 11:23:53 -0800407 FlowRule.Builder outer = DefaultFlowRule.builder()
alshabib56efe432016-02-25 17:57:24 -0500408 .withCookie(counter.getAndIncrement())
alshabibfd430b62015-12-16 18:56:38 -0800409 .forDevice(deviceId)
alshabibfd430b62015-12-16 18:56:38 -0800410 .makePermanent()
411 .withPriority(fwd.priority())
alshabibfa0dc662016-01-13 11:23:53 -0800412 .withSelector(buildSelector(inport, outerVlan))
alshabibfd430b62015-12-16 18:56:38 -0800413 .withTreatment(buildTreatment(popAndRewrite.getLeft(),
414 Instructions.transition(QQ_TABLE)));
alshabibfd430b62015-12-16 18:56:38 -0800415
alshabibfa0dc662016-01-13 11:23:53 -0800416 FlowRule.Builder inner = DefaultFlowRule.builder()
alshabib56efe432016-02-25 17:57:24 -0500417 .withCookie(counter.getAndIncrement())
alshabibfd430b62015-12-16 18:56:38 -0800418 .forDevice(deviceId)
alshabibfd430b62015-12-16 18:56:38 -0800419 .forTable(QQ_TABLE)
420 .makePermanent()
421 .withPriority(fwd.priority())
alshabib2f74f2c2016-01-14 13:29:35 -0800422 .withSelector(buildSelector(inport, innerVid))
alshabibe5075842016-02-04 13:28:33 -0800423 .withTreatment(buildTreatment(popAndRewrite.getLeft(),
alshabibfd430b62015-12-16 18:56:38 -0800424 output));
425
426 applyRules(fwd, inner, outer);
427
428 }
429
430 private void installUpstreamRules(ForwardingObjective fwd) {
431 List<Pair<Instruction, Instruction>> vlanOps =
432 vlanOps(fwd,
433 L2ModificationInstruction.L2SubType.VLAN_PUSH);
434
435 if (vlanOps == null) {
436 return;
437 }
438
439 Instruction output = fetchOutput(fwd, "upstream");
440
441 if (output == null) {
442 return;
443 }
444
445 Pair<Instruction, Instruction> innerPair = vlanOps.remove(0);
446
447 Pair<Instruction, Instruction> outerPair = vlanOps.remove(0);
448
449 FlowRule.Builder inner = DefaultFlowRule.builder()
alshabib56efe432016-02-25 17:57:24 -0500450 .withCookie(counter.getAndIncrement())
alshabibfd430b62015-12-16 18:56:38 -0800451 .forDevice(deviceId)
alshabibfd430b62015-12-16 18:56:38 -0800452 .makePermanent()
453 .withPriority(fwd.priority())
454 .withSelector(fwd.selector())
455 .withTreatment(buildTreatment(innerPair.getRight(),
456 Instructions.transition(QQ_TABLE)));
457
458 PortCriterion inPort = (PortCriterion)
459 fwd.selector().getCriterion(Criterion.Type.IN_PORT);
460
461 VlanId cVlanId = ((L2ModificationInstruction.ModVlanIdInstruction)
462 innerPair.getRight()).vlanId();
463
464 FlowRule.Builder outer = DefaultFlowRule.builder()
alshabib56efe432016-02-25 17:57:24 -0500465 .withCookie(counter.getAndIncrement())
alshabibfd430b62015-12-16 18:56:38 -0800466 .forDevice(deviceId)
alshabibfd430b62015-12-16 18:56:38 -0800467 .forTable(QQ_TABLE)
468 .makePermanent()
469 .withPriority(fwd.priority())
470 .withSelector(buildSelector(inPort,
471 Criteria.matchVlanId(cVlanId)))
472 .withTreatment(buildTreatment(outerPair.getLeft(),
473 outerPair.getRight(),
474 output));
475
476 applyRules(fwd, inner, outer);
477
478 }
479
480 private Instruction fetchOutput(ForwardingObjective fwd, String direction) {
481 Instruction output = fwd.treatment().allInstructions().stream()
482 .filter(i -> i.type() == Instruction.Type.OUTPUT)
483 .findFirst().orElse(null);
484
485 if (output == null) {
486 log.error("OLT {} rule has no output", direction);
487 fail(fwd, ObjectiveError.BADPARAMS);
488 return null;
489 }
490 return output;
491 }
492
493 private List<Pair<Instruction, Instruction>> vlanOps(ForwardingObjective fwd,
494 L2ModificationInstruction.L2SubType type) {
495
496 List<Pair<Instruction, Instruction>> vlanOps = findVlanOps(
497 fwd.treatment().allInstructions(), type);
498
499 if (vlanOps == null) {
500 String direction = type == L2ModificationInstruction.L2SubType.VLAN_POP
501 ? "downstream" : "upstream";
502 log.error("Missing vlan operations in {} forwarding: {}", direction, fwd);
503 fail(fwd, ObjectiveError.BADPARAMS);
504 return null;
505 }
506 return vlanOps;
507 }
508
509
510 private List<Pair<Instruction, Instruction>> findVlanOps(List<Instruction> instructions,
alshabibd61b77b2016-02-01 23:30:53 -0800511 L2ModificationInstruction.L2SubType type) {
alshabibfd430b62015-12-16 18:56:38 -0800512
513 List<Instruction> vlanPushs = findL2Instructions(
514 type,
515 instructions);
516 List<Instruction> vlanSets = findL2Instructions(
517 L2ModificationInstruction.L2SubType.VLAN_ID,
518 instructions);
519
520 if (vlanPushs.size() != vlanSets.size()) {
521 return null;
522 }
523
524 List<Pair<Instruction, Instruction>> pairs = Lists.newArrayList();
525
526 for (int i = 0; i < vlanPushs.size(); i++) {
527 pairs.add(new ImmutablePair<>(vlanPushs.get(i), vlanSets.get(i)));
528 }
529 return pairs;
530 }
531
532 private List<Instruction> findL2Instructions(L2ModificationInstruction.L2SubType subType,
533 List<Instruction> actions) {
534 return actions.stream()
535 .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
536 .filter(i -> ((L2ModificationInstruction) i).subtype() == subType)
537 .collect(Collectors.toList());
538 }
539
540 private void provisionEapol(FilteringObjective filter,
541 EthTypeCriterion ethType,
542 Instructions.OutputInstruction output) {
543
544 TrafficSelector selector = buildSelector(filter.key(), ethType);
545 TrafficTreatment treatment = buildTreatment(output);
546 buildAndApplyRule(filter, selector, treatment);
547
548 }
549
Jonathan Hart51539b82015-10-29 09:53:04 -0700550 private void provisionIgmp(FilteringObjective filter, EthTypeCriterion ethType,
alshabibfd430b62015-12-16 18:56:38 -0800551 IPProtocolCriterion ipProto,
552 Instructions.OutputInstruction output) {
553 TrafficSelector selector = buildSelector(filter.key(), ethType, ipProto);
554 TrafficTreatment treatment = buildTreatment(output);
555 buildAndApplyRule(filter, selector, treatment);
556 }
557
558 private void buildAndApplyRule(FilteringObjective filter, TrafficSelector selector,
559 TrafficTreatment treatment) {
560 FlowRule rule = DefaultFlowRule.builder()
alshabib56efe432016-02-25 17:57:24 -0500561 .withCookie(counter.getAndIncrement())
alshabibfd430b62015-12-16 18:56:38 -0800562 .forDevice(deviceId)
563 .forTable(0)
alshabibfd430b62015-12-16 18:56:38 -0800564 .makePermanent()
565 .withSelector(selector)
566 .withTreatment(treatment)
alshabibbb424232016-01-15 12:20:25 -0800567 .withPriority(filter.priority())
alshabibfd430b62015-12-16 18:56:38 -0800568 .build();
569
570 FlowRuleOperations.Builder opsBuilder = FlowRuleOperations.builder();
571
572 switch (filter.type()) {
573 case PERMIT:
574 opsBuilder.add(rule);
575 break;
576 case DENY:
577 opsBuilder.remove(rule);
578 break;
579 default:
580 log.warn("Unknown filter type : {}", filter.type());
581 fail(filter, ObjectiveError.UNSUPPORTED);
582 }
583
584 applyFlowRules(opsBuilder, filter);
585 }
586
587 private void applyRules(ForwardingObjective fwd,
588 FlowRule.Builder inner, FlowRule.Builder outer) {
589 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
590 switch (fwd.op()) {
591 case ADD:
592 builder.add(inner.build()).add(outer.build());
593 break;
594 case REMOVE:
595 builder.remove(inner.build()).remove(outer.build());
596 break;
597 case ADD_TO_EXISTING:
598 break;
599 case REMOVE_FROM_EXISTING:
600 break;
601 default:
602 log.warn("Unknown forwarding operation: {}", fwd.op());
603 }
604
605 applyFlowRules(builder, fwd);
606 }
607
608 private void applyFlowRules(FlowRuleOperations.Builder builder,
609 Objective objective) {
610 flowRuleService.apply(builder.build(new FlowRuleOperationsContext() {
611 @Override
612 public void onSuccess(FlowRuleOperations ops) {
613 pass(objective);
614 }
615
616 @Override
617 public void onError(FlowRuleOperations ops) {
618 fail(objective, ObjectiveError.FLOWINSTALLATIONFAILED);
619 }
620 }));
621 }
622
623 private Criterion filterForCriterion(Collection<Criterion> criteria, Criterion.Type type) {
624 return criteria.stream()
alshabibbb424232016-01-15 12:20:25 -0800625 .filter(c -> c.type().equals(type))
alshabibfd430b62015-12-16 18:56:38 -0800626 .limit(1)
627 .findFirst().orElse(null);
628 }
629
630 private TrafficSelector buildSelector(Criterion... criteria) {
631
632
633 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
634
635 for (Criterion c : criteria) {
636 sBuilder.add(c);
637 }
638
639 return sBuilder.build();
640 }
641
642 private TrafficTreatment buildTreatment(Instruction... instructions) {
643
644
645 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
646
647 for (Instruction i : instructions) {
648 tBuilder.add(i);
649 }
650
651 return tBuilder.build();
652 }
653
654
655 private void fail(Objective obj, ObjectiveError error) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -0800656 obj.context().ifPresent(context -> context.onError(obj, error));
alshabibfd430b62015-12-16 18:56:38 -0800657 }
658
659 private void pass(Objective obj) {
Sho SHIMIZUef7e2902016-02-12 18:38:29 -0800660 obj.context().ifPresent(context -> context.onSuccess(obj));
alshabibfd430b62015-12-16 18:56:38 -0800661 }
662
alshabib2cc73cb2015-06-30 20:26:56 -0700663
alshabibd61b77b2016-02-01 23:30:53 -0800664 private class InnerGroupListener implements GroupListener {
alshabib2cc73cb2015-06-30 20:26:56 -0700665 @Override
alshabibd61b77b2016-02-01 23:30:53 -0800666 public void event(GroupEvent event) {
667 if (event.type() == GroupEvent.Type.GROUP_ADDED) {
668 GroupKey key = event.subject().appCookie();
alshabib2cc73cb2015-06-30 20:26:56 -0700669
alshabibd61b77b2016-02-01 23:30:53 -0800670 NextObjective obj = pendingGroups.getIfPresent(key);
671 if (obj != null) {
672 flowObjectiveStore.putNextGroup(obj.id(), new OLTPipelineGroup(key));
673 pass(obj);
674 pendingGroups.invalidate(key);
675 }
676 }
alshabib2cc73cb2015-06-30 20:26:56 -0700677 }
678 }
679
alshabibd61b77b2016-02-01 23:30:53 -0800680 private static class OLTPipelineGroup implements NextGroup {
681
682 private final GroupKey key;
683
684 public OLTPipelineGroup(GroupKey key) {
685 this.key = key;
686 }
687
688 public GroupKey key() {
689 return key;
690 }
691
692 @Override
693 public byte[] data() {
694 return appKryo.serialize(key);
695 }
696
697 }
alshabib0ccde6d2015-05-30 18:22:36 -0700698}