blob: ef8fa8c40a9382e10f8f9908206a965614712f95 [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;
34import org.onosproject.incubator.net.virtual.NetworkId;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090035import org.onosproject.incubator.net.virtual.VirtualNetworkService;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070036import org.onosproject.incubator.net.virtual.VirtualPort;
Harold Huang7362e672017-04-19 10:00:11 +080037import org.onosproject.incubator.net.virtual.VirtualLink;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070038import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
39import org.onosproject.incubator.net.virtual.provider.InternalRoutingAlgorithm;
40import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProvider;
41import org.onosproject.incubator.net.virtual.provider.VirtualFlowRuleProviderService;
42import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
43import org.onosproject.net.ConnectPoint;
44import org.onosproject.net.DeviceId;
45import org.onosproject.net.Link;
46import org.onosproject.net.Path;
47import org.onosproject.net.PortNumber;
48import org.onosproject.net.device.DeviceService;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090049import org.onosproject.net.flow.CompletedBatchOperation;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070050import org.onosproject.net.flow.DefaultFlowEntry;
51import org.onosproject.net.flow.DefaultFlowRule;
52import org.onosproject.net.flow.DefaultTrafficSelector;
53import org.onosproject.net.flow.DefaultTrafficTreatment;
54import org.onosproject.net.flow.FlowEntry;
55import org.onosproject.net.flow.FlowRule;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090056import org.onosproject.net.flow.FlowRuleBatchEntry;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070057import org.onosproject.net.flow.FlowRuleBatchOperation;
58import org.onosproject.net.flow.FlowRuleEvent;
59import org.onosproject.net.flow.FlowRuleListener;
60import org.onosproject.net.flow.FlowRuleService;
61import org.onosproject.net.flow.TrafficSelector;
62import org.onosproject.net.flow.TrafficTreatment;
63import org.onosproject.net.flow.criteria.Criterion;
64import org.onosproject.net.flow.criteria.PortCriterion;
65import org.onosproject.net.flow.instructions.Instruction;
66import org.onosproject.net.flow.instructions.Instructions;
67import org.onosproject.net.provider.ProviderId;
68import org.onosproject.net.topology.TopologyService;
69import org.osgi.service.component.ComponentContext;
70import org.slf4j.Logger;
71
72import java.util.Dictionary;
73import java.util.HashSet;
74import java.util.Map;
75import java.util.Optional;
76import java.util.Set;
Harold Huang7362e672017-04-19 10:00:11 +080077import java.util.List;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090078import java.util.stream.Collectors;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070079
80import static com.google.common.base.Preconditions.checkNotNull;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090081import static com.google.common.collect.ImmutableSet.copyOf;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070082import static org.slf4j.LoggerFactory.getLogger;
83
84/**
85 * Provider that translate virtual flow rules into physical rules.
86 * Current implementation is based on FlowRules.
87 * This virtualize and de-virtualize virtual flow rules into physical flow rules.
88 * {@link org.onosproject.net.flow.FlowRule}
89 */
90@Component(immediate = true)
91@Service
92public class DefaultVirtualFlowRuleProvider extends AbstractVirtualProvider
93 implements VirtualFlowRuleProvider {
94
Yoonseon Hanc8089db2017-03-22 20:22:12 +090095 private static final String APP_ID_STR = "org.onosproject.virtual.vnet-flow_";
Yoonseon Hanc70b4e02016-10-20 15:24:33 -070096
97 private final Logger log = getLogger(getClass());
98
99 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 protected TopologyService topologyService;
101
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900103 protected VirtualNetworkService vnService;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
106 protected CoreService coreService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected FlowRuleService flowRuleService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
112 protected DeviceService deviceService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
115 protected VirtualProviderRegistryService providerRegistryService;
116
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900117 private InternalRoutingAlgorithm internalRoutingAlgorithm;
118 private InternalVirtualFlowRuleManager frm;
119 private ApplicationId appId;
120 private FlowRuleListener flowRuleListener;
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700121
122 /**
123 * Creates a provider with the supplied identifier.
124 */
125 public DefaultVirtualFlowRuleProvider() {
126 super(new ProviderId("vnet-flow", "org.onosproject.virtual.vnet-flow"));
127 }
128
129
130 @Activate
131 public void activate() {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900132 appId = coreService.registerApplication(APP_ID_STR);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700133
134 providerRegistryService.registerProvider(this);
135
136 flowRuleListener = new InternalFlowRuleListener();
137 flowRuleService.addListener(flowRuleListener);
138
139 internalRoutingAlgorithm = new DefaultInternalRoutingAlgorithm();
140 frm = new InternalVirtualFlowRuleManager();
141
142 log.info("Started");
143 }
144
145 @Deactivate
146 public void deactivate() {
147 flowRuleService.removeListener(flowRuleListener);
148 providerRegistryService.unregisterProvider(this);
149 }
150
151 @Modified
152 protected void modified(ComponentContext context) {
153 Dictionary<?, ?> properties = context.getProperties();
154 }
155
156 @Override
157 public void applyFlowRule(NetworkId networkId, FlowRule... flowRules) {
158 for (FlowRule flowRule : flowRules) {
159 devirtualize(networkId, flowRule).forEach(
160 r -> {
161 flowRuleService.applyFlowRules(r);
162 }
163 );
164 }
165 }
166
167 @Override
168 public void removeFlowRule(NetworkId networkId, FlowRule... flowRules) {
169 for (FlowRule flowRule : flowRules) {
170 devirtualize(networkId, flowRule).forEach(
171 r -> {
172 flowRuleService.removeFlowRules(r);
173 }
174 );
175 }
176 }
177
178 @Override
179 public void executeBatch(NetworkId networkId, FlowRuleBatchOperation batch) {
180 checkNotNull(batch);
181
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900182 for (FlowRuleBatchEntry fop : batch.getOperations()) {
183 devirtualize(networkId, fop.target())
184 .forEach(f -> flowRuleService.applyFlowRules(f));
185 }
186
187 //FIXME: check the success of the all batch operations
188 CompletedBatchOperation status =
189 new CompletedBatchOperation(true, Sets.newConcurrentHashSet(),
190 batch.deviceId());
191
192 VirtualFlowRuleProviderService providerService =
193 (VirtualFlowRuleProviderService) providerRegistryService
194 .getProviderService(networkId,
195 VirtualFlowRuleProvider.class);
196 providerService.batchOperationCompleted(batch.id(), status);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700197 }
198
199 public void setEmbeddingAlgorithm(InternalRoutingAlgorithm
200 internalRoutingAlgorithm) {
201 this.internalRoutingAlgorithm = internalRoutingAlgorithm;
202 }
203
204 /**
205 * Translate the requested physical flow rules into virtual flow rules.
206 *
207 * @param flowRule A virtual flow rule to be translated
208 * @return A flow rule for a specific virtual network
209 */
kdarapu93722ef2017-02-16 19:24:29 +0530210 private FlowRule virtualizeFlowRule(FlowRule flowRule) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700211 return frm.getVirtualRule(flowRule);
212 }
213
214 private FlowEntry virtualize(FlowEntry flowEntry) {
kdarapu93722ef2017-02-16 19:24:29 +0530215 FlowRule vRule = virtualizeFlowRule(flowEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700216 FlowEntry vEntry = new DefaultFlowEntry(vRule, flowEntry.state(),
217 flowEntry.life(),
218 flowEntry.packets(),
219 flowEntry.bytes());
220
221 return vEntry;
222 }
223
224 /**
225 * Translate the requested virtual flow rules into physical flow rules.
226 * The translation could be one to many.
227 *
228 * @param flowRule A flow rule from underlying data plane to be translated
229 * @return A set of flow rules for physical network
230 */
231 private Set<FlowRule> devirtualize(NetworkId networkId, FlowRule flowRule) {
232
233 Set<FlowRule> outRules = new HashSet<>();
234
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900235 Set<ConnectPoint> ingressPoints = extractIngressPoints(networkId,
236 flowRule.deviceId(),
237 flowRule.selector());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700238
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900239 ConnectPoint egressPoint = extractEgressPoints(networkId,
240 flowRule.deviceId(),
241 flowRule.treatment());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700242
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900243 if (egressPoint == null) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700244 return outRules;
245 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700246
247 TrafficSelector.Builder commonSelectorBuilder
248 = DefaultTrafficSelector.builder();
249 flowRule.selector().criteria().stream()
250 .filter(c -> c.type() != Criterion.Type.IN_PORT)
251 .forEach(c -> commonSelectorBuilder.add(c));
252 TrafficSelector commonSelector = commonSelectorBuilder.build();
253
254 TrafficTreatment.Builder commonTreatmentBuilder
255 = DefaultTrafficTreatment.builder();
256 flowRule.treatment().allInstructions().stream()
257 .filter(i -> i.type() != Instruction.Type.OUTPUT)
258 .forEach(i -> commonTreatmentBuilder.add(i));
259 TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
260
261 for (ConnectPoint ingressPoint : ingressPoints) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900262 if (egressPoint.port() == PortNumber.FLOOD) {
263 Set<ConnectPoint> outPoints = vnService
264 .getVirtualPorts(networkId, flowRule.deviceId())
265 .stream()
266 .map(VirtualPort::realizedBy)
267 .filter(p -> !p.equals(ingressPoint))
268 .collect(Collectors.toSet());
269
270 for (ConnectPoint outPoint : outPoints) {
271 outRules.addAll(generateRules(networkId, ingressPoint, outPoint,
272 commonSelector, commonTreatment, flowRule));
273 }
274 } else {
275 outRules.addAll(generateRules(networkId, ingressPoint, egressPoint,
276 commonSelector, commonTreatment, flowRule));
277 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700278 }
279
280 return outRules;
281 }
282
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900283 /**
284 * Extract ingress connect points of the physical network
285 * from the requested traffic selector.
286 *
287 * @param networkId the virtual network identifier
288 * @param deviceId the virtual device identifier
289 * @param selector the traffic selector to extract ingress point
290 * @return the set of ingress connect points of the physical network
291 */
292 private Set<ConnectPoint> extractIngressPoints(NetworkId networkId,
293 DeviceId deviceId,
294 TrafficSelector selector) {
295
296 Set<ConnectPoint> ingressPoints = new HashSet<>();
297
298 Set<VirtualPort> vPorts = vnService
299 .getVirtualPorts(networkId, deviceId);
300
301 PortCriterion portCriterion = ((PortCriterion) selector
302 .getCriterion(Criterion.Type.IN_PORT));
303
304 if (portCriterion != null) {
305 PortNumber vInPortNum = portCriterion.port();
306
307 Optional<ConnectPoint> optionalCp = vPorts.stream()
308 .filter(v -> v.number().equals(vInPortNum))
309 .map(VirtualPort::realizedBy).findFirst();
310 if (!optionalCp.isPresent()) {
311 log.warn("Port {} is not realized yet, in Network {}, Device {}",
312 vInPortNum, networkId, deviceId);
313 return ingressPoints;
314 }
315
316 ingressPoints.add(optionalCp.get());
317 } else {
318 for (VirtualPort vPort : vPorts) {
319 if (vPort.realizedBy() != null) {
320 ingressPoints.add(vPort.realizedBy());
321 } else {
322 log.warn("Port {} is not realized yet, in Network {}, " +
323 "Device {}",
324 vPort, networkId, deviceId);
325 }
326 }
327 }
328
329 return ingressPoints;
330 }
331
332 /**
333 * Extract egress connect point of the physical network
334 * from the requested traffic treatment.
335 *
336 * @param networkId the virtual network identifier
337 * @param deviceId the virtual device identifier
338 * @param treatment the traffic treatment to extract ingress point
339 * @return the egress connect point of the physical network
340 */
341 private ConnectPoint extractEgressPoints(NetworkId networkId,
342 DeviceId deviceId,
343 TrafficTreatment treatment) {
344
345 Set<VirtualPort> vPorts = vnService
346 .getVirtualPorts(networkId, deviceId);
347
348 PortNumber vOutPortNum = treatment.allInstructions().stream()
349 .filter(i -> i.type() == Instruction.Type.OUTPUT)
350 .map(i -> ((Instructions.OutputInstruction) i).port())
351 .findFirst().get();
352
353 Optional<ConnectPoint> optionalCpOut = vPorts.stream()
354 .filter(v -> v.number().equals(vOutPortNum))
355 .map(VirtualPort::realizedBy)
356 .findFirst();
357
358 if (!optionalCpOut.isPresent()) {
359 if (vOutPortNum.isLogical()) {
360 return new ConnectPoint(DeviceId.deviceId("vNet"), vOutPortNum);
361 }
362
363 log.warn("Port {} is not realized yet, in Network {}, Device {}",
364 vOutPortNum, networkId, deviceId);
365 return null;
366 }
367
368 return optionalCpOut.get();
369 }
370
371
372 /**
373 * Generates the corresponding flow rules for the physical network.
374 *
375 * @param networkId The virtual network identifier
376 * @param ingressPoint The ingress point of the physical network
377 * @param egressPoint The egress point of the physical network
378 * @param commonSelector A common traffic selector between the virtual
379 * and physical flow rules
380 * @param commonTreatment A common traffic treatment between the virtual
381 * and physical flow rules
382 * @param flowRule The virtual flow rule to be translated
383 * @return A set of flow rules for the physical network
384 */
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700385 private Set<FlowRule> generateRules(NetworkId networkId,
386 ConnectPoint ingressPoint,
387 ConnectPoint egressPoint,
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900388 TrafficSelector commonSelector,
389 TrafficTreatment commonTreatment,
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700390 FlowRule flowRule) {
391
392 Set<FlowRule> outRules = new HashSet<>();
393
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900394 if (ingressPoint.deviceId().equals(egressPoint.deviceId()) ||
395 egressPoint.port().isLogical()) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700396 //Traffic is handled inside a single physical switch
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700397
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900398 TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector
399 .builder(commonSelector)
400 .matchInPort(ingressPoint.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700401
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900402 TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment
Harold Huang7362e672017-04-19 10:00:11 +0800403 .builder(commonTreatment);
404
405 VirtualPort virtualIngressPort = vnService
406 .getVirtualPorts(networkId, flowRule.deviceId())
407 .stream()
408 .filter(p -> p.realizedBy().equals(ingressPoint))
409 .findFirst()
410 .get();
411
412 VirtualPort virtualEgressPort = vnService
413 .getVirtualPorts(networkId, flowRule.deviceId())
414 .stream()
415 .filter(p -> p.realizedBy().equals(egressPoint))
416 .findFirst()
417 .get();
418
419 ConnectPoint ingressCp = new ConnectPoint(virtualIngressPort.element().id(), virtualIngressPort.number());
420 ConnectPoint egressCp = new ConnectPoint(virtualEgressPort.element().id(), virtualEgressPort.number());
421
422 Optional<VirtualLink> optionalIngressLink = vnService
423 .getVirtualLinks(networkId)
424 .stream()
425 .filter(l -> l.dst().equals(ingressCp))
426 .findFirst();
427
428 Optional<VirtualLink> optionalEgressLink = vnService
429 .getVirtualLinks(networkId)
430 .stream()
431 .filter(l -> l.src().equals(egressCp))
432 .findFirst();
433
434 //Isolate traffic from different virtual networks with VLAN
435 if (!optionalIngressLink.isPresent() && !optionalEgressLink.isPresent()) {
436 treatmentBuilder.setOutput(egressPoint.port());
437 } else if (optionalIngressLink.isPresent() && !optionalEgressLink.isPresent()) {
438 selectorBuilder.matchVlanId(VlanId.vlanId(networkId.id().shortValue()));
439 treatmentBuilder.popVlan();
440 treatmentBuilder.setOutput(egressPoint.port());
441 } else if (!optionalIngressLink.isPresent() && optionalEgressLink.isPresent()) {
442 outRules.addAll(generateRulesOnPath(networkId, optionalEgressLink.get(),
443 commonSelector, commonTreatment, flowRule));
444 treatmentBuilder.pushVlan()
445 .setVlanId(VlanId.vlanId(networkId.id().shortValue()));
446 treatmentBuilder.setOutput(egressPoint.port());
447 } else if (optionalIngressLink.isPresent() && optionalEgressLink.isPresent()) {
448 outRules.addAll(generateRulesOnPath(networkId, optionalEgressLink.get(),
449 commonSelector, commonTreatment, flowRule));
450 selectorBuilder.matchVlanId(VlanId.vlanId(networkId.id().shortValue()));
451 treatmentBuilder.setOutput(egressPoint.port());
452 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700453
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900454 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
455 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
456 .forDevice(ingressPoint.deviceId())
457 .withSelector(selectorBuilder.build())
458 .withTreatment(treatmentBuilder.build())
459 .withPriority(flowRule.priority());
460
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700461 if (flowRule.isPermanent()) {
462 ruleBuilder.makePermanent();
463 } else {
464 ruleBuilder.makeTemporary(flowRule.timeout());
465 }
466
467 FlowRule rule = ruleBuilder.build();
468 frm.addIngressRule(flowRule, rule, networkId);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900469 outRules.add(rule);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700470 } else {
471 //Traffic is handled by multiple physical switches
472 //A tunnel is needed.
473
474 Path internalPath = internalRoutingAlgorithm
475 .findPath(ingressPoint, egressPoint);
476 checkNotNull(internalPath, "No path between " +
477 ingressPoint.toString() + " " + egressPoint.toString());
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900478
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700479 ConnectPoint outCp = internalPath.links().get(0).src();
480
481 //ingress point of tunnel
482 TrafficSelector.Builder selectorBuilder =
483 DefaultTrafficSelector.builder(commonSelector);
484 selectorBuilder.matchInPort(ingressPoint.port());
485
486 TrafficTreatment.Builder treatmentBuilder =
487 DefaultTrafficTreatment.builder(commonTreatment);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900488 //TODO: add the logic to check host location
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700489 treatmentBuilder.pushVlan()
490 .setVlanId(VlanId.vlanId(networkId.id().shortValue()));
491 treatmentBuilder.setOutput(outCp.port());
492
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900493 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
494 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
495 .forDevice(ingressPoint.deviceId())
496 .withSelector(selectorBuilder.build())
497 .withTreatment(treatmentBuilder.build())
498 .withPriority(flowRule.priority());
499
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700500 if (flowRule.isPermanent()) {
501 ruleBuilder.makePermanent();
502 } else {
503 ruleBuilder.makeTemporary(flowRule.timeout());
504 }
505
506 FlowRule rule = ruleBuilder.build();
507 frm.addIngressRule(flowRule, rule, networkId);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900508 outRules.add(rule);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700509
510 //routing inside tunnel
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900511 ConnectPoint inCp = internalPath.links().get(0).dst();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700512
513 if (internalPath.links().size() > 1) {
514 for (Link l : internalPath.links()
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900515 .subList(1, internalPath.links().size())) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700516
517 outCp = l.src();
518
519 selectorBuilder = DefaultTrafficSelector
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900520 .builder(commonSelector)
521 .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
522 .matchInPort(inCp.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700523
524 treatmentBuilder = DefaultTrafficTreatment
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900525 .builder(commonTreatment)
526 .setOutput(outCp.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700527
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900528 ruleBuilder = DefaultFlowRule.builder()
529 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
530 .forDevice(inCp.deviceId())
531 .withSelector(selectorBuilder.build())
532 .withTreatment(treatmentBuilder.build())
533 .withPriority(flowRule.priority());
534
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700535 if (flowRule.isPermanent()) {
536 ruleBuilder.makePermanent();
537 } else {
538 ruleBuilder.makeTemporary(flowRule.timeout());
539 }
540
541 outRules.add(ruleBuilder.build());
542 inCp = l.dst();
543 }
544 }
545
546 //egress point of tunnel
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900547 selectorBuilder = DefaultTrafficSelector.builder(commonSelector)
548 .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
549 .matchInPort(inCp.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700550
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900551 treatmentBuilder = DefaultTrafficTreatment.builder(commonTreatment)
552 .popVlan()
553 .setOutput(egressPoint.port());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700554
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900555 ruleBuilder = DefaultFlowRule.builder()
556 .fromApp(appId)
557 .forDevice(egressPoint.deviceId())
558 .withSelector(selectorBuilder.build())
559 .withTreatment(treatmentBuilder.build())
560 .withPriority(flowRule.priority());
561
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700562 if (flowRule.isPermanent()) {
563 ruleBuilder.makePermanent();
564 } else {
565 ruleBuilder.makeTemporary(flowRule.timeout());
566 }
567
568 outRules.add(ruleBuilder.build());
569 }
570
571 return outRules;
572 }
573
Harold Huang7362e672017-04-19 10:00:11 +0800574 /**
575 * Generate flow rules to the intermediate nodes on the physical path for a virtual link.
576 *
577 * @param networkId The virtual network identifier
578 * @param virtualLink A virtual link
579 * @param commonSelector A common traffic selector between the virtual
580 * and physical flow rules
581 * @param commonTreatment A common traffic treatment between the virtual
582 * and physical flow rules
583 * @param flowRule The virtual flow rule to be translated
584 * @return A set of flow rules for the path on physical network
585 */
586 private Set<FlowRule> generateRulesOnPath(NetworkId networkId,
587 VirtualLink virtualLink,
588 TrafficSelector commonSelector,
589 TrafficTreatment commonTreatment,
590 FlowRule flowRule) {
591
592 VirtualPort srcVirtualPort = vnService
593 .getVirtualPorts(networkId, virtualLink.src().deviceId())
594 .stream()
595 .filter(p -> p.number().equals(virtualLink.src().port()))
596 .findFirst()
597 .get();
598
599 VirtualPort dstVirtualPort = vnService
600 .getVirtualPorts(networkId, virtualLink.dst().deviceId())
601 .stream()
602 .filter(p -> p.number().equals(virtualLink.dst().port()))
603 .findFirst()
604 .get();
605 Set<FlowRule> outRules = new HashSet<>();
606 ConnectPoint srcCp = srcVirtualPort.realizedBy();
607 ConnectPoint dstCp = dstVirtualPort.realizedBy();
608
609 Path internalPath = internalRoutingAlgorithm
610 .findPath(srcCp, dstCp);
611 List<Link> links = internalPath.links();
612 if (internalPath != null && links.size() > 1) {
613 for (int i = 0; i < links.size() - 1; i++) {
614 ConnectPoint inCp = links.get(i).dst();
615 ConnectPoint outCp = links.get(i + 1).src();
616 TrafficSelector.Builder linkSelectorBuilder = DefaultTrafficSelector
617 .builder(commonSelector)
618 .matchVlanId(VlanId.vlanId(networkId.id().shortValue()))
619 .matchInPort(inCp.port());
620
621 TrafficTreatment.Builder linkTreatmentBuilder = DefaultTrafficTreatment
622 .builder(commonTreatment)
623 .setOutput(outCp.port());
624
625 FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
626 .fromApp(vnService.getVirtualNetworkApplicationId(networkId))
627 .forDevice(inCp.deviceId())
628 .withSelector(linkSelectorBuilder.build())
629 .withTreatment(linkTreatmentBuilder.build())
630 .withPriority(flowRule.priority());
631
632 if (flowRule.isPermanent()) {
633 ruleBuilder.makePermanent();
634 } else {
635 ruleBuilder.makeTemporary(flowRule.timeout());
636 }
637
638 outRules.add(ruleBuilder.build());
639 }
640 }
641 return outRules;
642 }
643
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700644 private class InternalFlowRuleListener implements FlowRuleListener {
645 @Override
646 public void event(FlowRuleEvent event) {
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700647 if ((event.type() == FlowRuleEvent.Type.RULE_ADDED) ||
648 (event.type() == FlowRuleEvent.Type.RULE_UPDATED)) {
649 if (frm.isVirtualIngressRule(event.subject())) {
650 NetworkId networkId = frm.getVirtualNetworkId(event.subject());
651 FlowEntry vEntry = getVirtualFlowEntry(event.subject());
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900652 frm.addOrUpdateFlowEntry(networkId, vEntry.deviceId(), vEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700653
654 VirtualFlowRuleProviderService providerService =
655 (VirtualFlowRuleProviderService) providerRegistryService
656 .getProviderService(networkId,
657 VirtualFlowRuleProvider.class);
658
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900659 ImmutableList.Builder<FlowEntry> builder = ImmutableList.builder();
660 builder.addAll(frm.getFlowEntries(networkId, vEntry.deviceId()));
661
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700662 providerService.pushFlowMetrics(vEntry.deviceId(), builder.build());
663 }
664 } else if (event.type() == FlowRuleEvent.Type.RULE_REMOVED) {
665 if (frm.isVirtualIngressRule(event.subject())) {
666 //FIXME confirm all physical rules are removed
667 NetworkId networkId = frm.getVirtualNetworkId(event.subject());
668 FlowEntry vEntry = getVirtualFlowEntry(event.subject());
669
670 VirtualFlowRuleProviderService providerService =
671 (VirtualFlowRuleProviderService) providerRegistryService
672 .getProviderService(networkId,
673 VirtualFlowRuleProvider.class);
674 providerService.flowRemoved(vEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700675 }
676 }
677 }
678
679 private FlowEntry getVirtualFlowEntry(FlowRule rule) {
680 FlowEntry entry = null;
681 for (FlowEntry fe :
682 flowRuleService.getFlowEntries(rule.deviceId())) {
683 if (rule.exactMatch(fe)) {
684 entry = fe;
685 }
686 }
687
688 FlowRule vRule = virtualize(entry);
689 FlowEntry vEntry = new DefaultFlowEntry(vRule, entry.state(),
690 entry.life(), entry.packets(),
691 entry.bytes());
692
693 return vEntry;
694 }
695 }
696
697 private class InternalVirtualFlowRuleManager {
698 /** <Virtual Network ID, Virtual Device ID, Virtual Flow Rules>.*/
699 final Table<NetworkId, DeviceId, Set<FlowRule>> flowRuleTable
700 = HashBasedTable.create();
701
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700702 /** <Virtual Network ID, Virtual Device ID, Virtual Flow Entries>.*/
703 final Table<NetworkId, DeviceId, Set<FlowEntry>> flowEntryTable
704 = HashBasedTable.create();
705
706 /** <Physical Flow Rule, Virtual Network ID>.*/
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900707 final Map<FlowRule, NetworkId> ingressRuleMap = Maps.newHashMap();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700708
709 /** <Physical Flow Rule, Virtual Virtual Flow Rule>.*/
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900710 final Map<FlowRule, FlowRule> virtualizationMap = Maps.newHashMap();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700711
712 private Iterable<FlowRule> getFlowRules(NetworkId networkId,
713 DeviceId deviceId) {
714 return flowRuleTable.get(networkId, deviceId);
715 }
716
717 private Iterable<FlowEntry> getFlowEntries(NetworkId networkId,
718 DeviceId deviceId) {
719 return flowEntryTable.get(networkId, deviceId);
720 }
721
722 private void addFlowRule(NetworkId networkId, DeviceId deviceId,
723 FlowRule flowRule) {
724 Set<FlowRule> set = flowRuleTable.get(networkId, deviceId);
725 if (set == null) {
726 set = Sets.newHashSet();
727 flowRuleTable.put(networkId, deviceId, set);
728 }
729 set.add(flowRule);
730 }
731
732 private void removeFlowRule(NetworkId networkId, DeviceId deviceId,
733 FlowRule flowRule) {
734 Set<FlowRule> set = flowRuleTable.get(networkId, deviceId);
735 if (set == null) {
736 return;
737 }
738 set.remove(flowRule);
739 }
740
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900741 private void addOrUpdateFlowEntry(NetworkId networkId, DeviceId deviceId,
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700742 FlowEntry flowEntry) {
743 Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId);
744 if (set == null) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900745 set = Sets.newConcurrentHashSet();
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700746 flowEntryTable.put(networkId, deviceId, set);
747 }
748
749 //Replace old entry with new one
750 set.stream().filter(fe -> fe.exactMatch(flowEntry))
751 .forEach(set::remove);
752 set.add(flowEntry);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700753 }
754
755 private void removeFlowEntry(NetworkId networkId, DeviceId deviceId,
756 FlowEntry flowEntry) {
757 Set<FlowEntry> set = flowEntryTable.get(networkId, deviceId);
758 if (set == null) {
759 return;
760 }
761 set.remove(flowEntry);
762 }
763
764 private void addIngressRule(FlowRule virtualRule, FlowRule physicalRule,
765 NetworkId networkId) {
766 ingressRuleMap.put(physicalRule, networkId);
767 virtualizationMap.put(physicalRule, virtualRule);
768 }
769
770 private FlowRule getVirtualRule(FlowRule physicalRule) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900771 return virtualizationMap.get(physicalRule);
772 }
773
774 private void removeIngressRule(FlowRule physicalRule) {
775 ingressRuleMap.remove(physicalRule);
776 virtualizationMap.remove(physicalRule);
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700777 }
778
779 private Set<FlowRule> getAllPhysicalRule() {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900780 return copyOf(virtualizationMap.keySet());
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700781 }
782
783 private NetworkId getVirtualNetworkId(FlowRule physicalRule) {
784 return ingressRuleMap.get(physicalRule);
785 }
786
787 /**
788 * Test the rule is the ingress rule for virtual rules.
789 *
790 * @param flowRule A flow rule from underlying data plane to be translated
791 * @return True when the rule is for ingress point for a virtual switch
792 */
793 private boolean isVirtualIngressRule(FlowRule flowRule) {
794 return ingressRuleMap.containsKey(flowRule);
795 }
Yoonseon Hanc70b4e02016-10-20 15:24:33 -0700796 }
797
798 private class DefaultInternalRoutingAlgorithm
799 implements InternalRoutingAlgorithm {
800
801 @Override
802 public Path findPath(ConnectPoint src, ConnectPoint dst) {
803 Set<Path> paths =
804 topologyService.getPaths(topologyService.currentTopology(),
805 src.deviceId(),
806 dst.deviceId());
807
808 if (paths.isEmpty()) {
809 return null;
810 }
811
812 //TODO the logic find the best path
813 return (Path) paths.toArray()[0];
814 }
815 }
816}