blob: 69f764eac8cbb3b4d2a037e44d392f0ae22b5fad [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 Hanc70b4e02016-10-20 15:24:33 -070021import com.google.common.collect.Maps;
22import com.google.common.collect.Sets;
23import com.google.common.collect.Table;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
27import org.apache.felix.scr.annotations.Modified;
28import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
30import org.apache.felix.scr.annotations.Service;
31import org.onlab.packet.VlanId;
32import org.onosproject.core.ApplicationId;
33import org.onosproject.core.CoreService;
Yoonseon Han5cf483a2017-04-19 23:14:00 -070034import org.onosproject.core.DefaultApplicationId;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070035import org.onosproject.incubator.net.virtual.NetworkId;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090036import org.onosproject.incubator.net.virtual.VirtualNetworkService;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070037import org.onosproject.incubator.net.virtual.VirtualPort;
Harold Huang7362e672017-04-19 10:00:11 +080038import org.onosproject.incubator.net.virtual.VirtualLink;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070039import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
40import org.onosproject.incubator.net.virtual.provider.InternalRoutingAlgorithm;
41import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider;
42import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProviderService;
43import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
44import org.onosproject.net.ConnectPoint;
45import org.onosproject.net.DeviceId;
46import org.onosproject.net.Link;
47import org.onosproject.net.Path;
48import org.onosproject.net.PortNumber;
49import org.onosproject.net.device.DeviceService;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090050import org.onosproject.net.flow.CompletedBatchOperation;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070051import org.onosproject.net.flow.DefaultFlowEntry;
52import org.onosproject.net.flow.DefaultFlowRule;
53import org.onosproject.net.flow.DefaultTrafficSelector;
54import org.onosproject.net.flow.DefaultTrafficTreatment;
55import org.onosproject.net.flow.FlowEntry;
56import org.onosproject.net.flow.FlowRule;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090057import org.onosproject.net.flow.FlowRuleBatchEntry;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070058import org.onosproject.net.flow.FlowRuleBatchOperation;
59import org.onosproject.net.flow.FlowRuleEvent;
60import org.onosproject.net.flow.FlowRuleListener;
61import org.onosproject.net.flow.FlowRuleService;
62import org.onosproject.net.flow.TrafficSelector;
63import org.onosproject.net.flow.TrafficTreatment;
64import org.onosproject.net.flow.criteria.Criterion;
65import org.onosproject.net.flow.criteria.PortCriterion;
66import org.onosproject.net.flow.instructions.Instruction;
67import org.onosproject.net.flow.instructions.Instructions;
68import org.onosproject.net.provider.ProviderId;
69import org.onosproject.net.topology.TopologyService;
70import org.osgi.service.component.ComponentContext;
71import org.slf4j.Logger;
72
73import java.util.Dictionary;
74import java.util.HashSet;
75import java.util.Map;
76import java.util.Optional;
77import java.util.Set;
Harold Huang7362e672017-04-19 10:00:11 +080078import java.util.List;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090079import java.util.stream.Collectors;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070080
81import static com.google.common.base.Preconditions.checkNotNull;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090082import static com.google.common.collect.ImmutableSet.copyOf;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070083import static org.slf4j.LoggerFactory.getLogger;
84
85/**
86 * Provider that translate virtual flow rules into physical rules.
87 * Current implementation is based on FlowRules.
88 * This virtualize and de-virtualize virtual flow rules into physical flow rules.
89 * {@link org.onosproject.net.flow.FlowRule}
90 */
91@Component(immediate = true)
92@Service
93public class DefaultVirtualFlowRuleProvider extends AbstractVirtualProvider
94 implements VirtualFlowRuleProvider {
95
Yoonseon Hanc8089db2017-03-22 20:22:12 +090096 private static final String APP_ID_STR = "org.onosproject.virtual.vnet-flow_";
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070097
98 private final Logger log = getLogger(getClass());
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected TopologyService topologyService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900104 protected VirtualNetworkService vnService;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected CoreService coreService;
108
109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected FlowRuleService flowRuleService;
111
112 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
113 protected DeviceService deviceService;
114
115 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
116 protected VirtualProviderRegistryService providerRegistryService;
117
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900118 private InternalRoutingAlgorithm internalRoutingAlgorithm;
119 private InternalVirtualFlowRuleManager frm;
120 private ApplicationId appId;
121 private FlowRuleListener flowRuleListener;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700122
123 /**
124 * Creates a provider with the supplied identifier.
125 */
126 public DefaultVirtualFlowRuleProvider() {
127 super(new ProviderId("vnet-flow", "org.onosproject.virtual.vnet-flow"));
128 }
129
130
131 @Activate
132 public void activate() {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900133 appId = coreService.registerApplication(APP_ID_STR);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700134
135 providerRegistryService.registerProvider(this);
136
137 flowRuleListener = new InternalFlowRuleListener();
138 flowRuleService.addListener(flowRuleListener);
139
140 internalRoutingAlgorithm = new DefaultInternalRoutingAlgorithm();
141 frm = new InternalVirtualFlowRuleManager();
142
143 log.info("Started");
144 }
145
146 @Deactivate
147 public void deactivate() {
148 flowRuleService.removeListener(flowRuleListener);
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700149 flowRuleService.removeFlowRulesById(appId);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700150 providerRegistryService.unregisterProvider(this);
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700151 log.info("Stopped");
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700152 }
153
154 @Modified
155 protected void modified(ComponentContext context) {
156 Dictionary<?, ?> properties = context.getProperties();
157 }
158
159 @Override
160 public void applyFlowRule(NetworkId networkId, FlowRule... flowRules) {
161 for (FlowRule flowRule : flowRules) {
162 devirtualize(networkId, flowRule).forEach(
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700163 r -> flowRuleService.applyFlowRules(r));
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700164 }
165 }
166
167 @Override
168 public void removeFlowRule(NetworkId networkId, FlowRule... flowRules) {
169 for (FlowRule flowRule : flowRules) {
170 devirtualize(networkId, flowRule).forEach(
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700171 r -> flowRuleService.removeFlowRules(r));
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700172 }
173 }
174
175 @Override
176 public void executeBatch(NetworkId networkId, FlowRuleBatchOperation batch) {
177 checkNotNull(batch);
178
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900179 for (FlowRuleBatchEntry fop : batch.getOperations()) {
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700180 switch (fop.operator()) {
181 case ADD:
182 devirtualize(networkId, fop.target())
183 .forEach(f -> flowRuleService.applyFlowRules(f));
184 break;
185 case REMOVE:
186 devirtualize(networkId, fop.target())
187 .forEach(f -> flowRuleService.removeFlowRules(f));
188 break;
189 case MODIFY:
190 break;
191 default:
192 break;
193 }
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900194 }
195
196 //FIXME: check the success of the all batch operations
197 CompletedBatchOperation status =
198 new CompletedBatchOperation(true, Sets.newConcurrentHashSet(),
199 batch.deviceId());
200
201 VirtualFlowRuleProviderService providerService =
202 (VirtualFlowRuleProviderService) providerRegistryService
203 .getProviderService(networkId,
204 VirtualFlowRuleProvider.class);
205 providerService.batchOperationCompleted(batch.id(), status);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700206 }
207
208 public void setEmbeddingAlgorithm(InternalRoutingAlgorithm
209 internalRoutingAlgorithm) {
210 this.internalRoutingAlgorithm = internalRoutingAlgorithm;
211 }
212
213 /**
214 * Translate the requested physical flow rules into virtual flow rules.
215 *
216 * @param flowRule A virtual flow rule to be translated
217 * @return A flow rule for a specific virtual network
218 */
kdarapu93722ef2017-02-16 19:24:29 +0530219 private FlowRule virtualizeFlowRule(FlowRule flowRule) {
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700220
221 FlowRule storedrule = frm.getVirtualRule(flowRule);
222
223 if (flowRule.reason() == FlowRule.FlowRemoveReason.NO_REASON) {
224 return storedrule;
225 } else {
226 return DefaultFlowRule.builder()
227 .withReason(flowRule.reason())
228 .withPriority(storedrule.priority())
229 .forDevice(storedrule.deviceId())
230 .forTable(storedrule.tableId())
231 .fromApp(new DefaultApplicationId(storedrule.appId(), null))
232 .withIdleTimeout(storedrule.timeout())
233 .withHardTimeout(storedrule.hardTimeout())
234 .withSelector(storedrule.selector())
235 .withTreatment(storedrule.treatment())
236 .build();
237 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700238 }
239
240 private FlowEntry virtualize(FlowEntry flowEntry) {
kdarapu93722ef2017-02-16 19:24:29 +0530241 FlowRule vRule = virtualizeFlowRule(flowEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700242 FlowEntry vEntry = new DefaultFlowEntry(vRule, flowEntry.state(),
243 flowEntry.life(),
244 flowEntry.packets(),
245 flowEntry.bytes());
246
247 return vEntry;
248 }
249
250 /**
251 * Translate the requested virtual flow rules into physical flow rules.
252 * The translation could be one to many.
253 *
254 * @param flowRule A flow rule from underlying data plane to be translated
255 * @return A set of flow rules for physical network
256 */
257 private Set<FlowRule> devirtualize(NetworkId networkId, FlowRule flowRule) {
258
259 Set<FlowRule> outRules = new HashSet<>();
260
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900261 Set<ConnectPoint> ingressPoints = extractIngressPoints(networkId,
262 flowRule.deviceId(),
263 flowRule.selector());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700264
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900265 ConnectPoint egressPoint = extractEgressPoints(networkId,
266 flowRule.deviceId(),
267 flowRule.treatment());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700268
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900269 if (egressPoint == null) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700270 return outRules;
271 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700272
273 TrafficSelector.Builder commonSelectorBuilder
274 = DefaultTrafficSelector.builder();
275 flowRule.selector().criteria().stream()
276 .filter(c -> c.type() != Criterion.Type.IN_PORT)
277 .forEach(c -> commonSelectorBuilder.add(c));
278 TrafficSelector commonSelector = commonSelectorBuilder.build();
279
280 TrafficTreatment.Builder commonTreatmentBuilder
281 = DefaultTrafficTreatment.builder();
282 flowRule.treatment().allInstructions().stream()
283 .filter(i -> i.type() != Instruction.Type.OUTPUT)
284 .forEach(i -> commonTreatmentBuilder.add(i));
285 TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
286
287 for (ConnectPoint ingressPoint : ingressPoints) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900288 if (egressPoint.port() == PortNumber.FLOOD) {
289 Set<ConnectPoint> outPoints = vnService
290 .getVirtualPorts(networkId, flowRule.deviceId())
291 .stream()
292 .map(VirtualPort::realizedBy)
293 .filter(p -> !p.equals(ingressPoint))
294 .collect(Collectors.toSet());
295
296 for (ConnectPoint outPoint : outPoints) {
297 outRules.addAll(generateRules(networkId, ingressPoint, outPoint,
298 commonSelector, commonTreatment, flowRule));
299 }
300 } else {
301 outRules.addAll(generateRules(networkId, ingressPoint, egressPoint,
302 commonSelector, commonTreatment, flowRule));
303 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700304 }
305
306 return outRules;
307 }
308
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900309 /**
310 * Extract ingress connect points of the physical network
311 * from the requested traffic selector.
312 *
313 * @param networkId the virtual network identifier
314 * @param deviceId the virtual device identifier
315 * @param selector the traffic selector to extract ingress point
316 * @return the set of ingress connect points of the physical network
317 */
318 private Set<ConnectPoint> extractIngressPoints(NetworkId networkId,
319 DeviceId deviceId,
320 TrafficSelector selector) {
321
322 Set<ConnectPoint> ingressPoints = new HashSet<>();
323
324 Set<VirtualPort> vPorts = vnService
325 .getVirtualPorts(networkId, deviceId);
326
327 PortCriterion portCriterion = ((PortCriterion) selector
328 .getCriterion(Criterion.Type.IN_PORT));
329
330 if (portCriterion != null) {
331 PortNumber vInPortNum = portCriterion.port();
332
333 Optional<ConnectPoint> optionalCp = vPorts.stream()
334 .filter(v -> v.number().equals(vInPortNum))
335 .map(VirtualPort::realizedBy).findFirst();
336 if (!optionalCp.isPresent()) {
337 log.warn("Port {} is not realized yet, in Network {}, Device {}",
338 vInPortNum, networkId, deviceId);
339 return ingressPoints;
340 }
341
342 ingressPoints.add(optionalCp.get());
343 } else {
344 for (VirtualPort vPort : vPorts) {
345 if (vPort.realizedBy() != null) {
346 ingressPoints.add(vPort.realizedBy());
347 } else {
348 log.warn("Port {} is not realized yet, in Network {}, " +
349 "Device {}",
350 vPort, networkId, deviceId);
351 }
352 }
353 }
354
355 return ingressPoints;
356 }
357
358 /**
359 * Extract egress connect point of the physical network
360 * from the requested traffic treatment.
361 *
362 * @param networkId the virtual network identifier
363 * @param deviceId the virtual device identifier
364 * @param treatment the traffic treatment to extract ingress point
365 * @return the egress connect point of the physical network
366 */
367 private ConnectPoint extractEgressPoints(NetworkId networkId,
368 DeviceId deviceId,
369 TrafficTreatment treatment) {
370
371 Set<VirtualPort> vPorts = vnService
372 .getVirtualPorts(networkId, deviceId);
373
374 PortNumber vOutPortNum = treatment.allInstructions().stream()
375 .filter(i -> i.type() == Instruction.Type.OUTPUT)
376 .map(i -> ((Instructions.OutputInstruction) i).port())
377 .findFirst().get();
378
379 Optional<ConnectPoint> optionalCpOut = vPorts.stream()
380 .filter(v -> v.number().equals(vOutPortNum))
381 .map(VirtualPort::realizedBy)
382 .findFirst();
383
384 if (!optionalCpOut.isPresent()) {
385 if (vOutPortNum.isLogical()) {
386 return new ConnectPoint(DeviceId.deviceId("vNet"), vOutPortNum);
387 }
388
389 log.warn("Port {} is not realized yet, in Network {}, Device {}",
390 vOutPortNum, networkId, deviceId);
391 return null;
392 }
393
394 return optionalCpOut.get();
395 }
396
397
398 /**
399 * Generates the corresponding flow rules for the physical network.
400 *
401 * @param networkId The virtual network identifier
402 * @param ingressPoint The ingress point of the physical network
403 * @param egressPoint The egress point of the physical network
404 * @param commonSelector A common traffic selector between the virtual
405 * and physical flow rules
406 * @param commonTreatment A common traffic treatment between the virtual
407 * and physical flow rules
408 * @param flowRule The virtual flow rule to be translated
409 * @return A set of flow rules for the physical network
410 */
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700411 private Set<FlowRule> generateRules(NetworkId networkId,
412 ConnectPoint ingressPoint,
413 ConnectPoint egressPoint,
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900414 TrafficSelector commonSelector,
415 TrafficTreatment commonTreatment,
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700416 FlowRule flowRule) {
417
418 Set<FlowRule> outRules = new HashSet<>();
419
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900420 if (ingressPoint.deviceId().equals(egressPoint.deviceId()) ||
421 egressPoint.port().isLogical()) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700422 //Traffic is handled inside a single physical switch
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700423
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900424 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector
425 .builder(commonSelector)
426 .matchInPort(ingressPoint.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700427
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900428 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
Harold Huang7362e672017-04-19 10:00:11 +0800429 .builder(commonTreatment);
430
431 VirtualPort virtualIngressPort = vnService
432 .getVirtualPorts(networkId, flowRule.deviceId())
433 .stream()
434 .filter(p -> p.realizedBy().equals(ingressPoint))
435 .findFirst()
436 .get();
437
438 VirtualPort virtualEgressPort = vnService
439 .getVirtualPorts(networkId, flowRule.deviceId())
440 .stream()
441 .filter(p -> p.realizedBy().equals(egressPoint))
442 .findFirst()
443 .get();
444
445 ConnectPoint ingressCp = new ConnectPoint(virtualIngressPort.element().id(), virtualIngressPort.number());
446 ConnectPoint egressCp = new ConnectPoint(virtualEgressPort.element().id(), virtualEgressPort.number());
447
448 Optional<VirtualLink> optionalIngressLink = vnService
449 .getVirtualLinks(networkId)
450 .stream()
451 .filter(l -> l.dst().equals(ingressCp))
452 .findFirst();
453
454 Optional<VirtualLink> optionalEgressLink = vnService
455 .getVirtualLinks(networkId)
456 .stream()
457 .filter(l -> l.src().equals(egressCp))
458 .findFirst();
459
460 //Isolate traffic from different virtual networks with VLAN
461 if (!optionalIngressLink.isPresent() && !optionalEgressLink.isPresent()) {
462 treatmentBuilder.setOutput(egressPoint.port());
463 } else if (optionalIngressLink.isPresent() && !optionalEgressLink.isPresent()) {
464 selectorBuilder.matchVlanId(VlanId.vlanId(networkId.id().shortValue()));
465 treatmentBuilder.popVlan();
466 treatmentBuilder.setOutput(egressPoint.port());
467 } else if (!optionalIngressLink.isPresent() && optionalEgressLink.isPresent()) {
468 outRules.addAll(generateRulesOnPath(networkId, optionalEgressLink.get(),
469 commonSelector, commonTreatment, flowRule));
470 treatmentBuilder.pushVlan()
471 .setVlanId(VlanId.vlanId(networkId.id().shortValue()));
472 treatmentBuilder.setOutput(egressPoint.port());
473 } else if (optionalIngressLink.isPresent() && optionalEgressLink.isPresent()) {
474 outRules.addAll(generateRulesOnPath(networkId, optionalEgressLink.get(),
475 commonSelector, commonTreatment, flowRule));
476 selectorBuilder.matchVlanId(VlanId.vlanId(networkId.id().shortValue()));
477 treatmentBuilder.setOutput(egressPoint.port());
478 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700479
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900480 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
481 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
482 .forDevice(ingressPoint.deviceId())
483 .withSelector(selectorBuilder.build())
484 .withTreatment(treatmentBuilder.build())
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700485 .withIdleTimeout(flowRule.timeout())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900486 .withPriority(flowRule.priority());
487
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700488 FlowRule rule = ruleBuilder.build();
489 frm.addIngressRule(flowRule, rule, networkId);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900490 outRules.add(rule);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700491 } else {
492 //Traffic is handled by multiple physical switches
493 //A tunnel is needed.
494
495 Path internalPath = internalRoutingAlgorithm
496 .findPath(ingressPoint, egressPoint);
497 checkNotNull(internalPath, "No path between " +
498 ingressPoint.toString() + " " + egressPoint.toString());
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900499
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700500 ConnectPoint outCp = internalPath.links().get(0).src();
501
502 //ingress point of tunnel
503 TrafficSelector.Builder selectorBuilder =
504 DefaultTrafficSelector.builder(commonSelector);
505 selectorBuilder.matchInPort(ingressPoint.port());
506
507 TrafficTreatment.Builder treatmentBuilder =
508 DefaultTrafficTreatment.builder(commonTreatment);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900509 //TODO: add the logic to check host location
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700510 treatmentBuilder.pushVlan()
511 .setVlanId(VlanId.vlanId(networkId.id().shortValue()));
512 treatmentBuilder.setOutput(outCp.port());
513
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900514 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
515 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
516 .forDevice(ingressPoint.deviceId())
517 .withSelector(selectorBuilder.build())
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700518 .withIdleTimeout(flowRule.timeout())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900519 .withTreatment(treatmentBuilder.build())
520 .withPriority(flowRule.priority());
521
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700522 FlowRule rule = ruleBuilder.build();
523 frm.addIngressRule(flowRule, rule, networkId);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900524 outRules.add(rule);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700525
526 //routing inside tunnel
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900527 ConnectPoint inCp = internalPath.links().get(0).dst();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700528
529 if (internalPath.links().size() > 1) {
530 for (Link l : internalPath.links()
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900531 .subList(1, internalPath.links().size())) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700532
533 outCp = l.src();
534
535 selectorBuilder = DefaultTrafficSelector
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900536 .builder(commonSelector)
537 .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
538 .matchInPort(inCp.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700539
540 treatmentBuilder = DefaultTrafficTreatment
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900541 .builder(commonTreatment)
542 .setOutput(outCp.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700543
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900544 ruleBuilder = DefaultFlowRule.builder()
545 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
546 .forDevice(inCp.deviceId())
547 .withSelector(selectorBuilder.build())
548 .withTreatment(treatmentBuilder.build())
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700549 .withIdleTimeout(flowRule.timeout())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900550 .withPriority(flowRule.priority());
551
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700552 outRules.add(ruleBuilder.build());
553 inCp = l.dst();
554 }
555 }
556
557 //egress point of tunnel
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900558 selectorBuilder = DefaultTrafficSelector.builder(commonSelector)
559 .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
560 .matchInPort(inCp.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700561
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900562 treatmentBuilder = DefaultTrafficTreatment.builder(commonTreatment)
563 .popVlan()
564 .setOutput(egressPoint.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700565
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900566 ruleBuilder = DefaultFlowRule.builder()
567 .fromApp(appId)
568 .forDevice(egressPoint.deviceId())
569 .withSelector(selectorBuilder.build())
570 .withTreatment(treatmentBuilder.build())
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700571 .withIdleTimeout(flowRule.timeout())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900572 .withPriority(flowRule.priority());
573
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700574 outRules.add(ruleBuilder.build());
575 }
576
577 return outRules;
578 }
579
Harold Huang7362e672017-04-19 10:00:11 +0800580 /**
581 * Generate flow rules to the intermediate nodes on the physical path for a virtual link.
582 *
583 * @param networkId The virtual network identifier
584 * @param virtualLink A virtual link
585 * @param commonSelector A common traffic selector between the virtual
586 * and physical flow rules
587 * @param commonTreatment A common traffic treatment between the virtual
588 * and physical flow rules
589 * @param flowRule The virtual flow rule to be translated
590 * @return A set of flow rules for the path on physical network
591 */
592 private Set<FlowRule> generateRulesOnPath(NetworkId networkId,
593 VirtualLink virtualLink,
594 TrafficSelector commonSelector,
595 TrafficTreatment commonTreatment,
596 FlowRule flowRule) {
597
598 VirtualPort srcVirtualPort = vnService
599 .getVirtualPorts(networkId, virtualLink.src().deviceId())
600 .stream()
601 .filter(p -> p.number().equals(virtualLink.src().port()))
602 .findFirst()
603 .get();
604
605 VirtualPort dstVirtualPort = vnService
606 .getVirtualPorts(networkId, virtualLink.dst().deviceId())
607 .stream()
608 .filter(p -> p.number().equals(virtualLink.dst().port()))
609 .findFirst()
610 .get();
611 Set<FlowRule> outRules = new HashSet<>();
612 ConnectPoint srcCp = srcVirtualPort.realizedBy();
613 ConnectPoint dstCp = dstVirtualPort.realizedBy();
614
615 Path internalPath = internalRoutingAlgorithm
616 .findPath(srcCp, dstCp);
617 List<Link> links = internalPath.links();
618 if (internalPath != null && links.size() > 1) {
619 for (int i = 0; i < links.size() - 1; i++) {
620 ConnectPoint inCp = links.get(i).dst();
621 ConnectPoint outCp = links.get(i + 1).src();
622 TrafficSelector.Builder linkSelectorBuilder = DefaultTrafficSelector
623 .builder(commonSelector)
624 .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
625 .matchInPort(inCp.port());
626
627 TrafficTreatment.Builder linkTreatmentBuilder = DefaultTrafficTreatment
628 .builder(commonTreatment)
629 .setOutput(outCp.port());
630
631 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
632 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
633 .forDevice(inCp.deviceId())
634 .withSelector(linkSelectorBuilder.build())
635 .withTreatment(linkTreatmentBuilder.build())
636 .withPriority(flowRule.priority());
637
638 if (flowRule.isPermanent()) {
639 ruleBuilder.makePermanent();
640 } else {
641 ruleBuilder.makeTemporary(flowRule.timeout());
642 }
643
644 outRules.add(ruleBuilder.build());
645 }
646 }
647 return outRules;
648 }
649
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700650 private class InternalFlowRuleListener implements FlowRuleListener {
651 @Override
652 public void event(FlowRuleEvent event) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700653 if ((event.type() == FlowRuleEvent.Type.RULE_ADDED) ||
654 (event.type() == FlowRuleEvent.Type.RULE_UPDATED)) {
655 if (frm.isVirtualIngressRule(event.subject())) {
656 NetworkId networkId = frm.getVirtualNetworkId(event.subject());
657 FlowEntry vEntry = getVirtualFlowEntry(event.subject());
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900658 frm.addOrUpdateFlowEntry(networkId, vEntry.deviceId(), vEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700659
660 VirtualFlowRuleProviderService providerService =
661 (VirtualFlowRuleProviderService) providerRegistryService
662 .getProviderService(networkId,
663 VirtualFlowRuleProvider.class);
664
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900665 ImmutableList.Builder<FlowEntry> builder = ImmutableList.builder();
666 builder.addAll(frm.getFlowEntries(networkId, vEntry.deviceId()));
667
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700668 providerService.pushFlowMetrics(vEntry.deviceId(), builder.build());
669 }
670 } else if (event.type() == FlowRuleEvent.Type.RULE_REMOVED) {
671 if (frm.isVirtualIngressRule(event.subject())) {
672 //FIXME confirm all physical rules are removed
673 NetworkId networkId = frm.getVirtualNetworkId(event.subject());
674 FlowEntry vEntry = getVirtualFlowEntry(event.subject());
675
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700676 frm.removeFlowEntry(networkId, vEntry.deviceId(), vEntry);
677 frm.removeFlowRule(networkId, vEntry.deviceId(), vEntry);
678
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700679 VirtualFlowRuleProviderService providerService =
680 (VirtualFlowRuleProviderService) providerRegistryService
681 .getProviderService(networkId,
682 VirtualFlowRuleProvider.class);
683 providerService.flowRemoved(vEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700684 }
685 }
686 }
687
688 private FlowEntry getVirtualFlowEntry(FlowRule rule) {
689 FlowEntry entry = null;
690 for (FlowEntry fe :
691 flowRuleService.getFlowEntries(rule.deviceId())) {
692 if (rule.exactMatch(fe)) {
693 entry = fe;
694 }
695 }
696
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700697 if (entry != null) {
698 return virtualize(entry);
699 } else {
700 return virtualize(new DefaultFlowEntry(rule,
701 FlowEntry.FlowEntryState.PENDING_REMOVE));
702 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700703 }
704 }
705
706 private class InternalVirtualFlowRuleManager {
707 /** <Virtual Network ID, Virtual Device ID, Virtual Flow Rules>.*/
708 final Table<NetworkId, DeviceId, Set<FlowRule>> flowRuleTable
709 = HashBasedTable.create();
710
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700711 /** <Virtual Network ID, Virtual Device ID, Virtual Flow Entries>.*/
712 final Table<NetworkId, DeviceId, Set<FlowEntry>> flowEntryTable
713 = HashBasedTable.create();
714
715 /** <Physical Flow Rule, Virtual Network ID>.*/
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900716 final Map<FlowRule, NetworkId> ingressRuleMap = Maps.newHashMap();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700717
718 /** <Physical Flow Rule, Virtual Virtual Flow Rule>.*/
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900719 final Map<FlowRule, FlowRule> virtualizationMap = Maps.newHashMap();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700720
721 private Iterable<FlowRule> getFlowRules(NetworkId networkId,
722 DeviceId deviceId) {
723 return flowRuleTable.get(networkId, deviceId);
724 }
725
726 private Iterable<FlowEntry> getFlowEntries(NetworkId networkId,
727 DeviceId deviceId) {
728 return flowEntryTable.get(networkId, deviceId);
729 }
730
731 private void addFlowRule(NetworkId networkId, DeviceId deviceId,
732 FlowRule flowRule) {
733 Set<FlowRule> set = flowRuleTable.get(networkId, deviceId);
734 if (set == null) {
735 set = Sets.newHashSet();
736 flowRuleTable.put(networkId, deviceId, set);
737 }
738 set.add(flowRule);
739 }
740
741 private void removeFlowRule(NetworkId networkId, DeviceId deviceId,
742 FlowRule flowRule) {
743 Set<FlowRule> set = flowRuleTable.get(networkId, deviceId);
744 if (set == null) {
745 return;
746 }
747 set.remove(flowRule);
748 }
749
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900750 private void addOrUpdateFlowEntry(NetworkId networkId, DeviceId deviceId,
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700751 FlowEntry flowEntry) {
752 Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId);
753 if (set == null) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900754 set = Sets.newConcurrentHashSet();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700755 flowEntryTable.put(networkId, deviceId, set);
756 }
757
758 //Replace old entry with new one
759 set.stream().filter(fe -> fe.exactMatch(flowEntry))
760 .forEach(set::remove);
761 set.add(flowEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700762 }
763
764 private void removeFlowEntry(NetworkId networkId, DeviceId deviceId,
765 FlowEntry flowEntry) {
766 Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId);
767 if (set == null) {
768 return;
769 }
770 set.remove(flowEntry);
771 }
772
773 private void addIngressRule(FlowRule virtualRule, FlowRule physicalRule,
774 NetworkId networkId) {
775 ingressRuleMap.put(physicalRule, networkId);
776 virtualizationMap.put(physicalRule, virtualRule);
777 }
778
779 private FlowRule getVirtualRule(FlowRule physicalRule) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900780 return virtualizationMap.get(physicalRule);
781 }
782
783 private void removeIngressRule(FlowRule physicalRule) {
784 ingressRuleMap.remove(physicalRule);
785 virtualizationMap.remove(physicalRule);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700786 }
787
788 private Set<FlowRule> getAllPhysicalRule() {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900789 return copyOf(virtualizationMap.keySet());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700790 }
791
792 private NetworkId getVirtualNetworkId(FlowRule physicalRule) {
793 return ingressRuleMap.get(physicalRule);
794 }
795
796 /**
797 * Test the rule is the ingress rule for virtual rules.
798 *
799 * @param flowRule A flow rule from underlying data plane to be translated
800 * @return True when the rule is for ingress point for a virtual switch
801 */
802 private boolean isVirtualIngressRule(FlowRule flowRule) {
803 return ingressRuleMap.containsKey(flowRule);
804 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700805 }
806
807 private class DefaultInternalRoutingAlgorithm
808 implements InternalRoutingAlgorithm {
809
810 @Override
811 public Path findPath(ConnectPoint src, ConnectPoint dst) {
812 Set<Path> paths =
813 topologyService.getPaths(topologyService.currentTopology(),
814 src.deviceId(),
815 dst.deviceId());
816
817 if (paths.isEmpty()) {
818 return null;
819 }
820
821 //TODO the logic find the best path
822 return (Path) paths.toArray()[0];
823 }
824 }
825}