blob: 4f9a8ffb3c6aff318efd69d064362f11f06734c4 [file] [log] [blame]
Yoonseon Hanc70b4e02016-10-20 15:24:33 -07001/*
2 * Copyright 2016-present 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 */
16
17package org.onosproject.incubator.net.virtual.impl.provider;
18
19import com.google.common.collect.HashBasedTable;
20import com.google.common.collect.ImmutableList;
Yoonseon Han997c8422017-04-24 16:20:03 -070021import com.google.common.collect.ImmutableSet;
22import com.google.common.collect.Lists;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070023import com.google.common.collect.Maps;
24import com.google.common.collect.Sets;
25import com.google.common.collect.Table;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070026import org.apache.felix.scr.annotations.Activate;
27import org.apache.felix.scr.annotations.Component;
28import org.apache.felix.scr.annotations.Deactivate;
29import org.apache.felix.scr.annotations.Modified;
30import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
32import org.apache.felix.scr.annotations.Service;
33import org.onlab.packet.VlanId;
34import org.onosproject.core.ApplicationId;
35import org.onosproject.core.CoreService;
Yoonseon Han5cf483a2017-04-19 23:14:00 -070036import org.onosproject.core.DefaultApplicationId;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070037import org.onosproject.incubator.net.virtual.NetworkId;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090038import org.onosproject.incubator.net.virtual.VirtualNetworkService;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070039import org.onosproject.incubator.net.virtual.VirtualPort;
Harold Huang7362e672017-04-19 10:00:11 +080040import org.onosproject.incubator.net.virtual.VirtualLink;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070041import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
42import org.onosproject.incubator.net.virtual.provider.InternalRoutingAlgorithm;
43import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider;
44import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProviderService;
45import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
46import org.onosproject.net.ConnectPoint;
47import org.onosproject.net.DeviceId;
48import org.onosproject.net.Link;
49import org.onosproject.net.Path;
50import org.onosproject.net.PortNumber;
51import org.onosproject.net.device.DeviceService;
Yoonseon Han997c8422017-04-24 16:20:03 -070052import org.onosproject.net.flow.BatchOperationEntry;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090053import org.onosproject.net.flow.CompletedBatchOperation;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070054import org.onosproject.net.flow.DefaultFlowEntry;
55import org.onosproject.net.flow.DefaultFlowRule;
56import org.onosproject.net.flow.DefaultTrafficSelector;
57import org.onosproject.net.flow.DefaultTrafficTreatment;
58import org.onosproject.net.flow.FlowEntry;
59import org.onosproject.net.flow.FlowRule;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090060import org.onosproject.net.flow.FlowRuleBatchEntry;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070061import org.onosproject.net.flow.FlowRuleBatchOperation;
62import org.onosproject.net.flow.FlowRuleEvent;
63import org.onosproject.net.flow.FlowRuleListener;
Yoonseon Han997c8422017-04-24 16:20:03 -070064import org.onosproject.net.flow.FlowRuleOperations;
65import org.onosproject.net.flow.FlowRuleOperationsContext;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070066import org.onosproject.net.flow.FlowRuleService;
67import org.onosproject.net.flow.TrafficSelector;
68import org.onosproject.net.flow.TrafficTreatment;
69import org.onosproject.net.flow.criteria.Criterion;
70import org.onosproject.net.flow.criteria.PortCriterion;
71import org.onosproject.net.flow.instructions.Instruction;
72import org.onosproject.net.flow.instructions.Instructions;
73import org.onosproject.net.provider.ProviderId;
74import org.onosproject.net.topology.TopologyService;
75import org.osgi.service.component.ComponentContext;
76import org.slf4j.Logger;
77
78import java.util.Dictionary;
79import java.util.HashSet;
80import java.util.Map;
81import java.util.Optional;
82import java.util.Set;
Harold Huang7362e672017-04-19 10:00:11 +080083import java.util.List;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090084import java.util.stream.Collectors;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070085
86import static com.google.common.base.Preconditions.checkNotNull;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090087import static com.google.common.collect.ImmutableSet.copyOf;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070088import static org.slf4j.LoggerFactory.getLogger;
89
90/**
91 * Provider that translate virtual flow rules into physical rules.
92 * Current implementation is based on FlowRules.
93 * This virtualize and de-virtualize virtual flow rules into physical flow rules.
94 * {@link org.onosproject.net.flow.FlowRule}
95 */
96@Component(immediate = true)
97@Service
98public class DefaultVirtualFlowRuleProvider extends AbstractVirtualProvider
99 implements VirtualFlowRuleProvider {
100
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900101 private static final String APP_ID_STR = "org.onosproject.virtual.vnet-flow_";
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700102
103 private final Logger log = getLogger(getClass());
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected TopologyService topologyService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900109 protected VirtualNetworkService vnService;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected CoreService coreService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected FlowRuleService flowRuleService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected DeviceService deviceService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
121 protected VirtualProviderRegistryService providerRegistryService;
122
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900123 private InternalRoutingAlgorithm internalRoutingAlgorithm;
124 private InternalVirtualFlowRuleManager frm;
125 private ApplicationId appId;
126 private FlowRuleListener flowRuleListener;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700127
128 /**
129 * Creates a provider with the supplied identifier.
130 */
131 public DefaultVirtualFlowRuleProvider() {
132 super(new ProviderId("vnet-flow", "org.onosproject.virtual.vnet-flow"));
133 }
134
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700135 @Activate
136 public void activate() {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900137 appId = coreService.registerApplication(APP_ID_STR);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700138
139 providerRegistryService.registerProvider(this);
140
141 flowRuleListener = new InternalFlowRuleListener();
142 flowRuleService.addListener(flowRuleListener);
143
144 internalRoutingAlgorithm = new DefaultInternalRoutingAlgorithm();
145 frm = new InternalVirtualFlowRuleManager();
146
147 log.info("Started");
148 }
149
150 @Deactivate
151 public void deactivate() {
152 flowRuleService.removeListener(flowRuleListener);
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700153 flowRuleService.removeFlowRulesById(appId);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700154 providerRegistryService.unregisterProvider(this);
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700155 log.info("Stopped");
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700156 }
157
158 @Modified
159 protected void modified(ComponentContext context) {
160 Dictionary<?, ?> properties = context.getProperties();
161 }
162
163 @Override
164 public void applyFlowRule(NetworkId networkId, FlowRule... flowRules) {
165 for (FlowRule flowRule : flowRules) {
166 devirtualize(networkId, flowRule).forEach(
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700167 r -> flowRuleService.applyFlowRules(r));
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700168 }
169 }
170
171 @Override
172 public void removeFlowRule(NetworkId networkId, FlowRule... flowRules) {
173 for (FlowRule flowRule : flowRules) {
174 devirtualize(networkId, flowRule).forEach(
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700175 r -> flowRuleService.removeFlowRules(r));
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700176 }
177 }
178
179 @Override
180 public void executeBatch(NetworkId networkId, FlowRuleBatchOperation batch) {
181 checkNotNull(batch);
182
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900183 for (FlowRuleBatchEntry fop : batch.getOperations()) {
Yoonseon Han997c8422017-04-24 16:20:03 -0700184 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
185
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700186 switch (fop.operator()) {
187 case ADD:
Yoonseon Han997c8422017-04-24 16:20:03 -0700188 devirtualize(networkId, fop.target()).forEach(builder::add);
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700189 break;
190 case REMOVE:
Yoonseon Han997c8422017-04-24 16:20:03 -0700191 devirtualize(networkId, fop.target()).forEach(builder::remove);
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700192 break;
193 case MODIFY:
Yoonseon Han997c8422017-04-24 16:20:03 -0700194 devirtualize(networkId, fop.target()).forEach(builder::modify);
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700195 break;
196 default:
197 break;
198 }
Yoonseon Han997c8422017-04-24 16:20:03 -0700199
200 flowRuleService.apply(builder.build(new FlowRuleOperationsContext() {
201 @Override
202 public void onSuccess(FlowRuleOperations ops) {
203 CompletedBatchOperation status =
204 new CompletedBatchOperation(true,
205 Sets.newConcurrentHashSet(),
206 batch.deviceId());
207
208 VirtualFlowRuleProviderService providerService =
209 (VirtualFlowRuleProviderService) providerRegistryService
210 .getProviderService(networkId,
211 VirtualFlowRuleProvider.class);
212 providerService.batchOperationCompleted(batch.id(), status);
213 }
214
215 @Override
216 public void onError(FlowRuleOperations ops) {
217 Set<FlowRule> failures = ImmutableSet.copyOf(
218 Lists.transform(batch.getOperations(),
219 BatchOperationEntry::target));
220
221 CompletedBatchOperation status =
222 new CompletedBatchOperation(false,
223 failures,
224 batch.deviceId());
225
226 VirtualFlowRuleProviderService providerService =
227 (VirtualFlowRuleProviderService) providerRegistryService
228 .getProviderService(networkId,
229 VirtualFlowRuleProvider.class);
230 providerService.batchOperationCompleted(batch.id(), status);
231 }
232 }));
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900233 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700234 }
235
236 public void setEmbeddingAlgorithm(InternalRoutingAlgorithm
237 internalRoutingAlgorithm) {
238 this.internalRoutingAlgorithm = internalRoutingAlgorithm;
239 }
240
241 /**
242 * Translate the requested physical flow rules into virtual flow rules.
243 *
244 * @param flowRule A virtual flow rule to be translated
245 * @return A flow rule for a specific virtual network
246 */
kdarapu93722ef2017-02-16 19:24:29 +0530247 private FlowRule virtualizeFlowRule(FlowRule flowRule) {
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700248
249 FlowRule storedrule = frm.getVirtualRule(flowRule);
250
251 if (flowRule.reason() == FlowRule.FlowRemoveReason.NO_REASON) {
252 return storedrule;
253 } else {
254 return DefaultFlowRule.builder()
255 .withReason(flowRule.reason())
256 .withPriority(storedrule.priority())
257 .forDevice(storedrule.deviceId())
258 .forTable(storedrule.tableId())
259 .fromApp(new DefaultApplicationId(storedrule.appId(), null))
260 .withIdleTimeout(storedrule.timeout())
261 .withHardTimeout(storedrule.hardTimeout())
262 .withSelector(storedrule.selector())
263 .withTreatment(storedrule.treatment())
264 .build();
265 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700266 }
267
268 private FlowEntry virtualize(FlowEntry flowEntry) {
kdarapu93722ef2017-02-16 19:24:29 +0530269 FlowRule vRule = virtualizeFlowRule(flowEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700270 FlowEntry vEntry = new DefaultFlowEntry(vRule, flowEntry.state(),
271 flowEntry.life(),
272 flowEntry.packets(),
273 flowEntry.bytes());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700274 return vEntry;
275 }
276
277 /**
278 * Translate the requested virtual flow rules into physical flow rules.
279 * The translation could be one to many.
280 *
281 * @param flowRule A flow rule from underlying data plane to be translated
282 * @return A set of flow rules for physical network
283 */
284 private Set<FlowRule> devirtualize(NetworkId networkId, FlowRule flowRule) {
285
286 Set<FlowRule> outRules = new HashSet<>();
287
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900288 Set<ConnectPoint> ingressPoints = extractIngressPoints(networkId,
289 flowRule.deviceId(),
290 flowRule.selector());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700291
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900292 ConnectPoint egressPoint = extractEgressPoints(networkId,
293 flowRule.deviceId(),
294 flowRule.treatment());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700295
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900296 if (egressPoint == null) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700297 return outRules;
298 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700299
300 TrafficSelector.Builder commonSelectorBuilder
301 = DefaultTrafficSelector.builder();
302 flowRule.selector().criteria().stream()
303 .filter(c -> c.type() != Criterion.Type.IN_PORT)
304 .forEach(c -> commonSelectorBuilder.add(c));
305 TrafficSelector commonSelector = commonSelectorBuilder.build();
306
307 TrafficTreatment.Builder commonTreatmentBuilder
308 = DefaultTrafficTreatment.builder();
309 flowRule.treatment().allInstructions().stream()
310 .filter(i -> i.type() != Instruction.Type.OUTPUT)
311 .forEach(i -> commonTreatmentBuilder.add(i));
312 TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
313
314 for (ConnectPoint ingressPoint : ingressPoints) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900315 if (egressPoint.port() == PortNumber.FLOOD) {
316 Set<ConnectPoint> outPoints = vnService
317 .getVirtualPorts(networkId, flowRule.deviceId())
318 .stream()
319 .map(VirtualPort::realizedBy)
320 .filter(p -> !p.equals(ingressPoint))
321 .collect(Collectors.toSet());
322
323 for (ConnectPoint outPoint : outPoints) {
324 outRules.addAll(generateRules(networkId, ingressPoint, outPoint,
325 commonSelector, commonTreatment, flowRule));
326 }
327 } else {
328 outRules.addAll(generateRules(networkId, ingressPoint, egressPoint,
329 commonSelector, commonTreatment, flowRule));
330 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700331 }
332
333 return outRules;
334 }
335
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900336 /**
337 * Extract ingress connect points of the physical network
338 * from the requested traffic selector.
339 *
340 * @param networkId the virtual network identifier
341 * @param deviceId the virtual device identifier
342 * @param selector the traffic selector to extract ingress point
343 * @return the set of ingress connect points of the physical network
344 */
345 private Set<ConnectPoint> extractIngressPoints(NetworkId networkId,
346 DeviceId deviceId,
347 TrafficSelector selector) {
348
349 Set<ConnectPoint> ingressPoints = new HashSet<>();
350
351 Set<VirtualPort> vPorts = vnService
352 .getVirtualPorts(networkId, deviceId);
353
354 PortCriterion portCriterion = ((PortCriterion) selector
355 .getCriterion(Criterion.Type.IN_PORT));
356
357 if (portCriterion != null) {
358 PortNumber vInPortNum = portCriterion.port();
359
360 Optional<ConnectPoint> optionalCp = vPorts.stream()
361 .filter(v -> v.number().equals(vInPortNum))
362 .map(VirtualPort::realizedBy).findFirst();
363 if (!optionalCp.isPresent()) {
364 log.warn("Port {} is not realized yet, in Network {}, Device {}",
365 vInPortNum, networkId, deviceId);
366 return ingressPoints;
367 }
368
369 ingressPoints.add(optionalCp.get());
370 } else {
371 for (VirtualPort vPort : vPorts) {
372 if (vPort.realizedBy() != null) {
373 ingressPoints.add(vPort.realizedBy());
374 } else {
375 log.warn("Port {} is not realized yet, in Network {}, " +
376 "Device {}",
377 vPort, networkId, deviceId);
378 }
379 }
380 }
381
382 return ingressPoints;
383 }
384
385 /**
386 * Extract egress connect point of the physical network
387 * from the requested traffic treatment.
388 *
389 * @param networkId the virtual network identifier
390 * @param deviceId the virtual device identifier
391 * @param treatment the traffic treatment to extract ingress point
392 * @return the egress connect point of the physical network
393 */
394 private ConnectPoint extractEgressPoints(NetworkId networkId,
395 DeviceId deviceId,
396 TrafficTreatment treatment) {
397
398 Set<VirtualPort> vPorts = vnService
399 .getVirtualPorts(networkId, deviceId);
400
401 PortNumber vOutPortNum = treatment.allInstructions().stream()
402 .filter(i -> i.type() == Instruction.Type.OUTPUT)
403 .map(i -> ((Instructions.OutputInstruction) i).port())
404 .findFirst().get();
405
406 Optional<ConnectPoint> optionalCpOut = vPorts.stream()
407 .filter(v -> v.number().equals(vOutPortNum))
408 .map(VirtualPort::realizedBy)
409 .findFirst();
410
411 if (!optionalCpOut.isPresent()) {
412 if (vOutPortNum.isLogical()) {
413 return new ConnectPoint(DeviceId.deviceId("vNet"), vOutPortNum);
414 }
415
416 log.warn("Port {} is not realized yet, in Network {}, Device {}",
417 vOutPortNum, networkId, deviceId);
418 return null;
419 }
420
421 return optionalCpOut.get();
422 }
423
424
425 /**
426 * Generates the corresponding flow rules for the physical network.
427 *
428 * @param networkId The virtual network identifier
429 * @param ingressPoint The ingress point of the physical network
430 * @param egressPoint The egress point of the physical network
431 * @param commonSelector A common traffic selector between the virtual
432 * and physical flow rules
433 * @param commonTreatment A common traffic treatment between the virtual
434 * and physical flow rules
435 * @param flowRule The virtual flow rule to be translated
436 * @return A set of flow rules for the physical network
437 */
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700438 private Set<FlowRule> generateRules(NetworkId networkId,
439 ConnectPoint ingressPoint,
440 ConnectPoint egressPoint,
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900441 TrafficSelector commonSelector,
442 TrafficTreatment commonTreatment,
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700443 FlowRule flowRule) {
444
445 Set<FlowRule> outRules = new HashSet<>();
446
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900447 if (ingressPoint.deviceId().equals(egressPoint.deviceId()) ||
448 egressPoint.port().isLogical()) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700449 //Traffic is handled inside a single physical switch
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700450
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900451 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector
452 .builder(commonSelector)
453 .matchInPort(ingressPoint.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700454
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900455 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
Harold Huang7362e672017-04-19 10:00:11 +0800456 .builder(commonTreatment);
457
458 VirtualPort virtualIngressPort = vnService
459 .getVirtualPorts(networkId, flowRule.deviceId())
460 .stream()
461 .filter(p -> p.realizedBy().equals(ingressPoint))
462 .findFirst()
463 .get();
464
465 VirtualPort virtualEgressPort = vnService
466 .getVirtualPorts(networkId, flowRule.deviceId())
467 .stream()
468 .filter(p -> p.realizedBy().equals(egressPoint))
469 .findFirst()
470 .get();
471
472 ConnectPoint ingressCp = new ConnectPoint(virtualIngressPort.element().id(), virtualIngressPort.number());
473 ConnectPoint egressCp = new ConnectPoint(virtualEgressPort.element().id(), virtualEgressPort.number());
474
475 Optional<VirtualLink> optionalIngressLink = vnService
476 .getVirtualLinks(networkId)
477 .stream()
478 .filter(l -> l.dst().equals(ingressCp))
479 .findFirst();
480
481 Optional<VirtualLink> optionalEgressLink = vnService
482 .getVirtualLinks(networkId)
483 .stream()
484 .filter(l -> l.src().equals(egressCp))
485 .findFirst();
486
487 //Isolate traffic from different virtual networks with VLAN
488 if (!optionalIngressLink.isPresent() && !optionalEgressLink.isPresent()) {
489 treatmentBuilder.setOutput(egressPoint.port());
490 } else if (optionalIngressLink.isPresent() && !optionalEgressLink.isPresent()) {
491 selectorBuilder.matchVlanId(VlanId.vlanId(networkId.id().shortValue()));
492 treatmentBuilder.popVlan();
493 treatmentBuilder.setOutput(egressPoint.port());
494 } else if (!optionalIngressLink.isPresent() && optionalEgressLink.isPresent()) {
495 outRules.addAll(generateRulesOnPath(networkId, optionalEgressLink.get(),
496 commonSelector, commonTreatment, flowRule));
497 treatmentBuilder.pushVlan()
498 .setVlanId(VlanId.vlanId(networkId.id().shortValue()));
499 treatmentBuilder.setOutput(egressPoint.port());
500 } else if (optionalIngressLink.isPresent() && optionalEgressLink.isPresent()) {
501 outRules.addAll(generateRulesOnPath(networkId, optionalEgressLink.get(),
502 commonSelector, commonTreatment, flowRule));
503 selectorBuilder.matchVlanId(VlanId.vlanId(networkId.id().shortValue()));
504 treatmentBuilder.setOutput(egressPoint.port());
505 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700506
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900507 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
508 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
509 .forDevice(ingressPoint.deviceId())
510 .withSelector(selectorBuilder.build())
511 .withTreatment(treatmentBuilder.build())
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700512 .withIdleTimeout(flowRule.timeout())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900513 .withPriority(flowRule.priority());
514
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700515 FlowRule rule = ruleBuilder.build();
516 frm.addIngressRule(flowRule, rule, networkId);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900517 outRules.add(rule);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700518 } else {
519 //Traffic is handled by multiple physical switches
520 //A tunnel is needed.
521
522 Path internalPath = internalRoutingAlgorithm
523 .findPath(ingressPoint, egressPoint);
524 checkNotNull(internalPath, "No path between " +
525 ingressPoint.toString() + " " + egressPoint.toString());
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900526
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700527 ConnectPoint outCp = internalPath.links().get(0).src();
528
529 //ingress point of tunnel
530 TrafficSelector.Builder selectorBuilder =
531 DefaultTrafficSelector.builder(commonSelector);
532 selectorBuilder.matchInPort(ingressPoint.port());
533
534 TrafficTreatment.Builder treatmentBuilder =
535 DefaultTrafficTreatment.builder(commonTreatment);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900536 //TODO: add the logic to check host location
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700537 treatmentBuilder.pushVlan()
538 .setVlanId(VlanId.vlanId(networkId.id().shortValue()));
539 treatmentBuilder.setOutput(outCp.port());
540
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900541 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
542 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
543 .forDevice(ingressPoint.deviceId())
544 .withSelector(selectorBuilder.build())
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700545 .withIdleTimeout(flowRule.timeout())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900546 .withTreatment(treatmentBuilder.build())
547 .withPriority(flowRule.priority());
548
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700549 FlowRule rule = ruleBuilder.build();
550 frm.addIngressRule(flowRule, rule, networkId);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900551 outRules.add(rule);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700552
553 //routing inside tunnel
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900554 ConnectPoint inCp = internalPath.links().get(0).dst();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700555
556 if (internalPath.links().size() > 1) {
557 for (Link l : internalPath.links()
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900558 .subList(1, internalPath.links().size())) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700559
560 outCp = l.src();
561
562 selectorBuilder = DefaultTrafficSelector
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900563 .builder(commonSelector)
564 .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
565 .matchInPort(inCp.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700566
567 treatmentBuilder = DefaultTrafficTreatment
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900568 .builder(commonTreatment)
569 .setOutput(outCp.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700570
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900571 ruleBuilder = DefaultFlowRule.builder()
572 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
573 .forDevice(inCp.deviceId())
574 .withSelector(selectorBuilder.build())
575 .withTreatment(treatmentBuilder.build())
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700576 .withIdleTimeout(flowRule.timeout())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900577 .withPriority(flowRule.priority());
578
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700579 outRules.add(ruleBuilder.build());
580 inCp = l.dst();
581 }
582 }
583
584 //egress point of tunnel
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900585 selectorBuilder = DefaultTrafficSelector.builder(commonSelector)
586 .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
587 .matchInPort(inCp.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700588
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900589 treatmentBuilder = DefaultTrafficTreatment.builder(commonTreatment)
590 .popVlan()
591 .setOutput(egressPoint.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700592
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900593 ruleBuilder = DefaultFlowRule.builder()
594 .fromApp(appId)
595 .forDevice(egressPoint.deviceId())
596 .withSelector(selectorBuilder.build())
597 .withTreatment(treatmentBuilder.build())
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700598 .withIdleTimeout(flowRule.timeout())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900599 .withPriority(flowRule.priority());
600
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700601 outRules.add(ruleBuilder.build());
602 }
603
604 return outRules;
605 }
606
Harold Huang7362e672017-04-19 10:00:11 +0800607 /**
608 * Generate flow rules to the intermediate nodes on the physical path for a virtual link.
609 *
610 * @param networkId The virtual network identifier
611 * @param virtualLink A virtual link
612 * @param commonSelector A common traffic selector between the virtual
613 * and physical flow rules
614 * @param commonTreatment A common traffic treatment between the virtual
615 * and physical flow rules
616 * @param flowRule The virtual flow rule to be translated
617 * @return A set of flow rules for the path on physical network
618 */
619 private Set<FlowRule> generateRulesOnPath(NetworkId networkId,
620 VirtualLink virtualLink,
621 TrafficSelector commonSelector,
622 TrafficTreatment commonTreatment,
623 FlowRule flowRule) {
624
625 VirtualPort srcVirtualPort = vnService
626 .getVirtualPorts(networkId, virtualLink.src().deviceId())
627 .stream()
628 .filter(p -> p.number().equals(virtualLink.src().port()))
629 .findFirst()
630 .get();
631
632 VirtualPort dstVirtualPort = vnService
633 .getVirtualPorts(networkId, virtualLink.dst().deviceId())
634 .stream()
635 .filter(p -> p.number().equals(virtualLink.dst().port()))
636 .findFirst()
637 .get();
638 Set<FlowRule> outRules = new HashSet<>();
639 ConnectPoint srcCp = srcVirtualPort.realizedBy();
640 ConnectPoint dstCp = dstVirtualPort.realizedBy();
641
642 Path internalPath = internalRoutingAlgorithm
643 .findPath(srcCp, dstCp);
644 List<Link> links = internalPath.links();
645 if (internalPath != null && links.size() > 1) {
646 for (int i = 0; i < links.size() - 1; i++) {
647 ConnectPoint inCp = links.get(i).dst();
648 ConnectPoint outCp = links.get(i + 1).src();
649 TrafficSelector.Builder linkSelectorBuilder = DefaultTrafficSelector
650 .builder(commonSelector)
651 .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
652 .matchInPort(inCp.port());
653
654 TrafficTreatment.Builder linkTreatmentBuilder = DefaultTrafficTreatment
655 .builder(commonTreatment)
656 .setOutput(outCp.port());
657
658 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
659 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
660 .forDevice(inCp.deviceId())
661 .withSelector(linkSelectorBuilder.build())
662 .withTreatment(linkTreatmentBuilder.build())
663 .withPriority(flowRule.priority());
664
665 if (flowRule.isPermanent()) {
666 ruleBuilder.makePermanent();
667 } else {
668 ruleBuilder.makeTemporary(flowRule.timeout());
669 }
670
671 outRules.add(ruleBuilder.build());
672 }
673 }
674 return outRules;
675 }
676
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700677 private class InternalFlowRuleListener implements FlowRuleListener {
678 @Override
679 public void event(FlowRuleEvent event) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700680 if ((event.type() == FlowRuleEvent.Type.RULE_ADDED) ||
681 (event.type() == FlowRuleEvent.Type.RULE_UPDATED)) {
682 if (frm.isVirtualIngressRule(event.subject())) {
683 NetworkId networkId = frm.getVirtualNetworkId(event.subject());
684 FlowEntry vEntry = getVirtualFlowEntry(event.subject());
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900685 frm.addOrUpdateFlowEntry(networkId, vEntry.deviceId(), vEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700686
687 VirtualFlowRuleProviderService providerService =
688 (VirtualFlowRuleProviderService) providerRegistryService
689 .getProviderService(networkId,
690 VirtualFlowRuleProvider.class);
691
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900692 ImmutableList.Builder<FlowEntry> builder = ImmutableList.builder();
693 builder.addAll(frm.getFlowEntries(networkId, vEntry.deviceId()));
694
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700695 providerService.pushFlowMetrics(vEntry.deviceId(), builder.build());
696 }
697 } else if (event.type() == FlowRuleEvent.Type.RULE_REMOVED) {
698 if (frm.isVirtualIngressRule(event.subject())) {
699 //FIXME confirm all physical rules are removed
700 NetworkId networkId = frm.getVirtualNetworkId(event.subject());
701 FlowEntry vEntry = getVirtualFlowEntry(event.subject());
702
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700703 frm.removeFlowEntry(networkId, vEntry.deviceId(), vEntry);
704 frm.removeFlowRule(networkId, vEntry.deviceId(), vEntry);
705
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700706 VirtualFlowRuleProviderService providerService =
707 (VirtualFlowRuleProviderService) providerRegistryService
708 .getProviderService(networkId,
709 VirtualFlowRuleProvider.class);
710 providerService.flowRemoved(vEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700711 }
712 }
713 }
714
715 private FlowEntry getVirtualFlowEntry(FlowRule rule) {
716 FlowEntry entry = null;
717 for (FlowEntry fe :
718 flowRuleService.getFlowEntries(rule.deviceId())) {
719 if (rule.exactMatch(fe)) {
720 entry = fe;
721 }
722 }
723
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700724 if (entry != null) {
725 return virtualize(entry);
726 } else {
727 return virtualize(new DefaultFlowEntry(rule,
728 FlowEntry.FlowEntryState.PENDING_REMOVE));
729 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700730 }
731 }
732
733 private class InternalVirtualFlowRuleManager {
734 /** <Virtual Network ID, Virtual Device ID, Virtual Flow Rules>.*/
735 final Table<NetworkId, DeviceId, Set<FlowRule>> flowRuleTable
736 = HashBasedTable.create();
737
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700738 /** <Virtual Network ID, Virtual Device ID, Virtual Flow Entries>.*/
739 final Table<NetworkId, DeviceId, Set<FlowEntry>> flowEntryTable
740 = HashBasedTable.create();
741
742 /** <Physical Flow Rule, Virtual Network ID>.*/
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900743 final Map<FlowRule, NetworkId> ingressRuleMap = Maps.newHashMap();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700744
745 /** <Physical Flow Rule, Virtual Virtual Flow Rule>.*/
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900746 final Map<FlowRule, FlowRule> virtualizationMap = Maps.newHashMap();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700747
748 private Iterable<FlowRule> getFlowRules(NetworkId networkId,
749 DeviceId deviceId) {
750 return flowRuleTable.get(networkId, deviceId);
751 }
752
753 private Iterable<FlowEntry> getFlowEntries(NetworkId networkId,
754 DeviceId deviceId) {
755 return flowEntryTable.get(networkId, deviceId);
756 }
757
758 private void addFlowRule(NetworkId networkId, DeviceId deviceId,
759 FlowRule flowRule) {
760 Set<FlowRule> set = flowRuleTable.get(networkId, deviceId);
761 if (set == null) {
762 set = Sets.newHashSet();
763 flowRuleTable.put(networkId, deviceId, set);
764 }
765 set.add(flowRule);
766 }
767
768 private void removeFlowRule(NetworkId networkId, DeviceId deviceId,
769 FlowRule flowRule) {
770 Set<FlowRule> set = flowRuleTable.get(networkId, deviceId);
771 if (set == null) {
772 return;
773 }
774 set.remove(flowRule);
775 }
776
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900777 private void addOrUpdateFlowEntry(NetworkId networkId, DeviceId deviceId,
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700778 FlowEntry flowEntry) {
779 Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId);
780 if (set == null) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900781 set = Sets.newConcurrentHashSet();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700782 flowEntryTable.put(networkId, deviceId, set);
783 }
784
785 //Replace old entry with new one
786 set.stream().filter(fe -> fe.exactMatch(flowEntry))
787 .forEach(set::remove);
788 set.add(flowEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700789 }
790
791 private void removeFlowEntry(NetworkId networkId, DeviceId deviceId,
792 FlowEntry flowEntry) {
793 Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId);
794 if (set == null) {
795 return;
796 }
797 set.remove(flowEntry);
798 }
799
800 private void addIngressRule(FlowRule virtualRule, FlowRule physicalRule,
801 NetworkId networkId) {
802 ingressRuleMap.put(physicalRule, networkId);
803 virtualizationMap.put(physicalRule, virtualRule);
804 }
805
806 private FlowRule getVirtualRule(FlowRule physicalRule) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900807 return virtualizationMap.get(physicalRule);
808 }
809
810 private void removeIngressRule(FlowRule physicalRule) {
811 ingressRuleMap.remove(physicalRule);
812 virtualizationMap.remove(physicalRule);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700813 }
814
815 private Set<FlowRule> getAllPhysicalRule() {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900816 return copyOf(virtualizationMap.keySet());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700817 }
818
819 private NetworkId getVirtualNetworkId(FlowRule physicalRule) {
820 return ingressRuleMap.get(physicalRule);
821 }
822
823 /**
824 * Test the rule is the ingress rule for virtual rules.
825 *
826 * @param flowRule A flow rule from underlying data plane to be translated
827 * @return True when the rule is for ingress point for a virtual switch
828 */
829 private boolean isVirtualIngressRule(FlowRule flowRule) {
830 return ingressRuleMap.containsKey(flowRule);
831 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700832 }
833
834 private class DefaultInternalRoutingAlgorithm
835 implements InternalRoutingAlgorithm {
836
837 @Override
838 public Path findPath(ConnectPoint src, ConnectPoint dst) {
839 Set<Path> paths =
840 topologyService.getPaths(topologyService.currentTopology(),
841 src.deviceId(),
842 dst.deviceId());
843
844 if (paths.isEmpty()) {
845 return null;
846 }
847
848 //TODO the logic find the best path
849 return (Path) paths.toArray()[0];
850 }
851 }
852}