blob: 242d94c55477246dd630240ecea9f4cfa976c37c [file] [log] [blame]
Hyunsun Moone7e4bb32016-05-16 04:32:45 -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 */
alshabibb4d31712016-06-01 18:51:03 -070016package org.opencord.cordvtn.impl;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070017
18import org.apache.felix.scr.annotations.Component;
19import org.apache.felix.scr.annotations.Reference;
20import org.apache.felix.scr.annotations.ReferenceCardinality;
21import org.onlab.packet.Ethernet;
22import org.onlab.packet.Ip4Address;
23import org.onlab.packet.Ip4Prefix;
alshabibb4d31712016-06-01 18:51:03 -070024import org.opencord.cordvtn.api.CordVtnConfig;
25import org.opencord.cordvtn.api.CordVtnNode;
26import org.opencord.cordvtn.api.CordVtnService;
27import org.opencord.cordvtn.api.Instance;
28import org.opencord.cordvtn.api.InstanceHandler;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070029import org.onosproject.core.ApplicationId;
30import org.onosproject.core.CoreService;
31import org.onosproject.mastership.MastershipService;
32import org.onosproject.net.Host;
33import org.onosproject.net.PortNumber;
34import org.onosproject.net.config.NetworkConfigEvent;
35import org.onosproject.net.config.NetworkConfigListener;
36import org.onosproject.net.config.NetworkConfigRegistry;
37import org.onosproject.net.flow.DefaultFlowRule;
38import org.onosproject.net.flow.DefaultTrafficSelector;
39import org.onosproject.net.flow.DefaultTrafficTreatment;
40import org.onosproject.net.flow.FlowRule;
41import org.onosproject.net.flow.TrafficSelector;
42import org.onosproject.net.flow.TrafficTreatment;
43import org.onosproject.net.flow.instructions.ExtensionTreatment;
44import org.onosproject.net.host.HostEvent;
45import org.onosproject.net.host.HostListener;
46import org.onosproject.net.host.HostService;
47import org.onosproject.xosclient.api.OpenStackAccess;
48import org.onosproject.xosclient.api.VtnService;
49import org.onosproject.xosclient.api.VtnServiceApi;
50import org.onosproject.xosclient.api.VtnServiceId;
51import org.onosproject.xosclient.api.XosAccess;
52import org.onosproject.xosclient.api.XosClientService;
53import org.slf4j.Logger;
54
55import java.util.Objects;
56import java.util.Set;
57import java.util.concurrent.ExecutorService;
58import java.util.stream.Collectors;
59import java.util.stream.StreamSupport;
60
61import static com.google.common.base.Preconditions.checkNotNull;
Hyunsun Moone7e4bb32016-05-16 04:32:45 -070062import static org.onosproject.xosclient.api.VtnService.NetworkType.MANAGEMENT;
63import static org.slf4j.LoggerFactory.getLogger;
64
65/**
66 * Provides default virtual network connectivity for service instances.
67 */
68@Component(immediate = true)
69public abstract class CordVtnInstanceHandler implements InstanceHandler {
70
71 protected final Logger log = getLogger(getClass());
72
73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected CoreService coreService;
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected MastershipService mastershipService;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected NetworkConfigRegistry configRegistry;
81
82 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
83 protected HostService hostService;
84
85 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
86 protected XosClientService xosClient;
87
88 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
89 protected CordVtnNodeManager nodeManager;
90
91 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
92 protected CordVtnPipeline pipeline;
93
94 protected static final String OPENSTACK_ACCESS_ERROR = "OpenStack access is not configured";
95 protected static final String XOS_ACCESS_ERROR = "XOS access is not configured";
96
97 protected XosAccess xosAccess = null;
98 protected OpenStackAccess osAccess = null;
99 protected ApplicationId appId;
100 protected VtnService.ServiceType serviceType;
101 protected ExecutorService eventExecutor;
102
103 protected HostListener hostListener = new InternalHostListener();
104 protected NetworkConfigListener configListener = new InternalConfigListener();
105
106 protected void activate() {
107 // sub class should set service type and event executor in its activate method
108 appId = coreService.registerApplication(CordVtnService.CORDVTN_APP_ID);
109
110 hostService.addListener(hostListener);
111 configRegistry.addListener(configListener);
112
113 log.info("Started");
114 }
115
116 protected void deactivate() {
117 hostService.removeListener(hostListener);
118 configRegistry.removeListener(configListener);
119 eventExecutor.shutdown();
120
121 log.info("Stopped");
122 }
123
124 @Override
125 public void instanceDetected(Instance instance) {
126 log.info("Instance is detected {}", instance);
127
128 VtnService service = getVtnService(instance.serviceId());
129 if (service == null) {
130 log.warn("Failed to get VtnService for {}", instance);
131 return;
132 }
133
134 if (service.networkType().equals(MANAGEMENT)) {
135 managementNetworkRules(instance, service, true);
136 }
137
138 defaultConnectionRules(instance, service, true);
139 }
140
141 @Override
142 public void instanceRemoved(Instance instance) {
143 log.info("Instance is removed {}", instance);
144
145 VtnService service = getVtnService(instance.serviceId());
146 if (service == null) {
147 log.warn("Failed to get VtnService for {}", instance);
148 return;
149 }
150
151 if (service.networkType().equals(MANAGEMENT)) {
152 managementNetworkRules(instance, service, false);
153 }
154
155 // TODO check if any stale management network rules are
156 defaultConnectionRules(instance, service, false);
157 }
158
159 protected VtnService getVtnService(VtnServiceId serviceId) {
160 checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
161 checkNotNull(xosAccess, XOS_ACCESS_ERROR);
162
163 // TODO remove openstack access when XOS provides all information
164 VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
165 VtnService service = serviceApi.service(serviceId, osAccess);
166 if (service == null) {
167 log.warn("Failed to get VtnService for {}", serviceId);
168 }
169 return service;
170 }
171
172 protected Set<Instance> getInstances(VtnServiceId serviceId) {
173 return StreamSupport.stream(hostService.getHosts().spliterator(), false)
174 .filter(host -> Objects.equals(
175 serviceId.id(),
176 host.annotations().value(Instance.SERVICE_ID)))
177 .map(Instance::of)
178 .collect(Collectors.toSet());
179 }
180
181 private void defaultConnectionRules(Instance instance, VtnService service, boolean install) {
182 long vni = service.vni();
183 Ip4Prefix serviceIpRange = service.subnet().getIp4Prefix();
184
185 inPortRule(instance, install);
186 dstIpRule(instance, vni, install);
187 tunnelInRule(instance, vni, install);
188
189 if (install) {
190 directAccessRule(serviceIpRange, serviceIpRange, true);
191 serviceIsolationRule(serviceIpRange, true);
192 } else if (getInstances(service.id()).isEmpty()) {
193 directAccessRule(serviceIpRange, serviceIpRange, false);
194 serviceIsolationRule(serviceIpRange, false);
195 }
196 }
197
198 private void managementNetworkRules(Instance instance, VtnService service, boolean install) {
199
200 managementPerInstanceRule(instance, install);
201 if (install) {
202 managementBaseRule(instance, service, true);
203 } else if (!hostService.getConnectedHosts(instance.deviceId()).stream()
204 .filter(host -> Instance.of(host).serviceId().equals(service.id()))
205 .findAny()
206 .isPresent()) {
207 managementBaseRule(instance, service, false);
208 }
209 }
210
211 private void managementBaseRule(Instance instance, VtnService service, boolean install) {
212 TrafficSelector selector = DefaultTrafficSelector.builder()
213 .matchEthType(Ethernet.TYPE_ARP)
214 .matchArpTpa(service.serviceIp().getIp4Address())
215 .build();
216
217 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
218 .setOutput(PortNumber.LOCAL)
219 .build();
220
221 FlowRule flowRule = DefaultFlowRule.builder()
222 .fromApp(appId)
223 .withSelector(selector)
224 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700225 .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700226 .forDevice(instance.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700227 .forTable(CordVtnPipeline.TABLE_ZERO)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700228 .makePermanent()
229 .build();
230
231 pipeline.processFlowRule(install, flowRule);
232
233 selector = DefaultTrafficSelector.builder()
234 .matchInPort(PortNumber.LOCAL)
235 .matchEthType(Ethernet.TYPE_IPV4)
236 .matchIPDst(service.subnet())
237 .build();
238
239 treatment = DefaultTrafficTreatment.builder()
alshabibb4d31712016-06-01 18:51:03 -0700240 .transition(CordVtnPipeline.TABLE_DST_IP)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700241 .build();
242
243 flowRule = DefaultFlowRule.builder()
244 .fromApp(appId)
245 .withSelector(selector)
246 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700247 .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700248 .forDevice(instance.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700249 .forTable(CordVtnPipeline.TABLE_ZERO)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700250 .makePermanent()
251 .build();
252
253 pipeline.processFlowRule(install, flowRule);
254
255 selector = DefaultTrafficSelector.builder()
256 .matchEthType(Ethernet.TYPE_IPV4)
257 .matchIPDst(service.serviceIp().toIpPrefix())
258 .build();
259
260 treatment = DefaultTrafficTreatment.builder()
261 .setOutput(PortNumber.LOCAL)
262 .build();
263
264 flowRule = DefaultFlowRule.builder()
265 .fromApp(appId)
266 .withSelector(selector)
267 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700268 .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700269 .forDevice(instance.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700270 .forTable(CordVtnPipeline.TABLE_ACCESS_TYPE)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700271 .makePermanent()
272 .build();
273
274 pipeline.processFlowRule(install, flowRule);
275 }
276
277 private void managementPerInstanceRule(Instance instance, boolean install) {
278 TrafficSelector selector = DefaultTrafficSelector.builder()
279 .matchInPort(PortNumber.LOCAL)
280 .matchEthType(Ethernet.TYPE_ARP)
281 .matchArpTpa(instance.ipAddress().getIp4Address())
282 .build();
283
284 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
285 .setOutput(instance.portNumber())
286 .build();
287
288 FlowRule flowRule = DefaultFlowRule.builder()
289 .fromApp(appId)
290 .withSelector(selector)
291 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700292 .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700293 .forDevice(instance.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700294 .forTable(CordVtnPipeline.TABLE_ZERO)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700295 .makePermanent()
296 .build();
297
298 pipeline.processFlowRule(install, flowRule);
299 }
300
301 private void inPortRule(Instance instance, boolean install) {
302 TrafficSelector selector = DefaultTrafficSelector.builder()
303 .matchInPort(instance.portNumber())
304 .matchEthType(Ethernet.TYPE_IPV4)
305 .matchIPSrc(instance.ipAddress().toIpPrefix())
306 .build();
307
308 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
alshabibb4d31712016-06-01 18:51:03 -0700309 .transition(CordVtnPipeline.TABLE_ACCESS_TYPE)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700310 .build();
311
312
313 FlowRule flowRule = DefaultFlowRule.builder()
314 .fromApp(appId)
315 .withSelector(selector)
316 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700317 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700318 .forDevice(instance.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700319 .forTable(CordVtnPipeline.TABLE_IN_PORT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700320 .makePermanent()
321 .build();
322
323 pipeline.processFlowRule(install, flowRule);
324
325 selector = DefaultTrafficSelector.builder()
326 .matchInPort(instance.portNumber())
327 .build();
328
329 treatment = DefaultTrafficTreatment.builder()
alshabibb4d31712016-06-01 18:51:03 -0700330 .transition(CordVtnPipeline.TABLE_IN_SERVICE)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700331 .build();
332
333 flowRule = DefaultFlowRule.builder()
334 .fromApp(appId)
335 .withSelector(selector)
336 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700337 .withPriority(CordVtnPipeline.PRIORITY_LOW)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700338 .forDevice(instance.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700339 .forTable(CordVtnPipeline.TABLE_IN_PORT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700340 .makePermanent()
341 .build();
342
343 pipeline.processFlowRule(install, flowRule);
344 }
345
346 private void dstIpRule(Instance instance, long vni, boolean install) {
347 Ip4Address tunnelIp = nodeManager.dpIp(instance.deviceId()).getIp4Address();
348
349 TrafficSelector selector = DefaultTrafficSelector.builder()
350 .matchEthType(Ethernet.TYPE_IPV4)
351 .matchIPDst(instance.ipAddress().toIpPrefix())
352 .build();
353
354 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
355 .setEthDst(instance.mac())
356 .setOutput(instance.portNumber())
357 .build();
358
359 FlowRule flowRule = DefaultFlowRule.builder()
360 .fromApp(appId)
361 .withSelector(selector)
362 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700363 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700364 .forDevice(instance.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700365 .forTable(CordVtnPipeline.TABLE_DST_IP)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700366 .makePermanent()
367 .build();
368
369 pipeline.processFlowRule(install, flowRule);
370
371 for (CordVtnNode node : nodeManager.completeNodes()) {
372 if (node.intBrId().equals(instance.deviceId())) {
373 continue;
374 }
375
376 ExtensionTreatment tunnelDst = pipeline.tunnelDstTreatment(node.intBrId(), tunnelIp);
377 if (tunnelDst == null) {
378 continue;
379 }
380
381 treatment = DefaultTrafficTreatment.builder()
382 .setEthDst(instance.mac())
383 .setTunnelId(vni)
384 .extension(tunnelDst, node.intBrId())
385 .setOutput(nodeManager.tunnelPort(node.intBrId()))
386 .build();
387
388 flowRule = DefaultFlowRule.builder()
389 .fromApp(appId)
390 .withSelector(selector)
391 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700392 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700393 .forDevice(node.intBrId())
alshabibb4d31712016-06-01 18:51:03 -0700394 .forTable(CordVtnPipeline.TABLE_DST_IP)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700395 .makePermanent()
396 .build();
397
398 pipeline.processFlowRule(install, flowRule);
399 }
400 }
401
402 private void tunnelInRule(Instance instance, long vni, boolean install) {
403 TrafficSelector selector = DefaultTrafficSelector.builder()
404 .matchTunnelId(vni)
405 .matchEthDst(instance.mac())
406 .build();
407
408 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
409 .setOutput(instance.portNumber())
410 .build();
411
412 FlowRule flowRule = DefaultFlowRule.builder()
413 .fromApp(appId)
414 .withSelector(selector)
415 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700416 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700417 .forDevice(instance.deviceId())
alshabibb4d31712016-06-01 18:51:03 -0700418 .forTable(CordVtnPipeline.TABLE_TUNNEL_IN)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700419 .makePermanent()
420 .build();
421
422 pipeline.processFlowRule(install, flowRule);
423 }
424
425 private void directAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange, boolean install) {
426 TrafficSelector selector = DefaultTrafficSelector.builder()
427 .matchEthType(Ethernet.TYPE_IPV4)
428 .matchIPSrc(srcRange)
429 .matchIPDst(dstRange)
430 .build();
431
432 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
alshabibb4d31712016-06-01 18:51:03 -0700433 .transition(CordVtnPipeline.TABLE_DST_IP)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700434 .build();
435
436
437 nodeManager.completeNodes().stream().forEach(node -> {
438 FlowRule flowRuleDirect = DefaultFlowRule.builder()
439 .fromApp(appId)
440 .withSelector(selector)
441 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700442 .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700443 .forDevice(node.intBrId())
alshabibb4d31712016-06-01 18:51:03 -0700444 .forTable(CordVtnPipeline.TABLE_ACCESS_TYPE)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700445 .makePermanent()
446 .build();
447
448 pipeline.processFlowRule(install, flowRuleDirect);
449 });
450 }
451
452 private void serviceIsolationRule(Ip4Prefix dstRange, boolean install) {
453 TrafficSelector selector = DefaultTrafficSelector.builder()
454 .matchEthType(Ethernet.TYPE_IPV4)
455 .matchIPDst(dstRange)
456 .build();
457
458 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
459 .drop()
460 .build();
461
462 nodeManager.completeNodes().stream().forEach(node -> {
463 FlowRule flowRuleDirect = DefaultFlowRule.builder()
464 .fromApp(appId)
465 .withSelector(selector)
466 .withTreatment(treatment)
alshabibb4d31712016-06-01 18:51:03 -0700467 .withPriority(CordVtnPipeline.PRIORITY_LOW)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700468 .forDevice(node.intBrId())
alshabibb4d31712016-06-01 18:51:03 -0700469 .forTable(CordVtnPipeline.TABLE_ACCESS_TYPE)
Hyunsun Moone7e4bb32016-05-16 04:32:45 -0700470 .makePermanent()
471 .build();
472
473 pipeline.processFlowRule(install, flowRuleDirect);
474 });
475 }
476
477 protected void readConfiguration() {
478 CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
479 if (config == null) {
480 log.debug("No configuration found");
481 return;
482 }
483 osAccess = config.openstackAccess();
484 xosAccess = config.xosAccess();
485 }
486
487 public class InternalHostListener implements HostListener {
488
489 @Override
490 public void event(HostEvent event) {
491 Host host = event.subject();
492 if (!mastershipService.isLocalMaster(host.location().deviceId())) {
493 // do not allow to proceed without mastership
494 return;
495 }
496
497 Instance instance = Instance.of(host);
498 if (!Objects.equals(instance.serviceType(), serviceType)) {
499 // not my service instance, do nothing
500 return;
501 }
502
503 switch (event.type()) {
504 case HOST_UPDATED:
505 case HOST_ADDED:
506 eventExecutor.execute(() -> instanceDetected(instance));
507 break;
508 case HOST_REMOVED:
509 eventExecutor.execute(() -> instanceRemoved(instance));
510 break;
511 default:
512 break;
513 }
514 }
515 }
516
517 public class InternalConfigListener implements NetworkConfigListener {
518
519 @Override
520 public void event(NetworkConfigEvent event) {
521 if (!event.configClass().equals(CordVtnConfig.class)) {
522 return;
523 }
524
525 switch (event.type()) {
526 case CONFIG_ADDED:
527 case CONFIG_UPDATED:
528 readConfiguration();
529 break;
530 default:
531 break;
532 }
533 }
534 }
535}