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