blob: 6904761458d3db7fea1aec559cf815807593cd4b [file] [log] [blame]
samuela5e17fc2015-07-29 15:46:40 +08001/*
2 * Copyright 2015 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.vtn.impl;
17
18import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
19import static org.onlab.util.Tools.groupedThreads;
20import static org.slf4j.LoggerFactory.getLogger;
21
22import java.util.HashSet;
23import java.util.List;
24import java.util.Set;
25import java.util.concurrent.ScheduledExecutorService;
26
27import org.apache.felix.scr.annotations.Activate;
28import org.apache.felix.scr.annotations.Component;
29import org.apache.felix.scr.annotations.Deactivate;
30import org.apache.felix.scr.annotations.Reference;
31import org.apache.felix.scr.annotations.ReferenceCardinality;
32import org.apache.felix.scr.annotations.Service;
33import org.onlab.packet.IpAddress;
34import org.onlab.packet.MacAddress;
35import org.onlab.util.KryoNamespace;
36import org.onosproject.app.vtnrsc.SegmentationId;
37import org.onosproject.app.vtnrsc.TenantNetwork;
38import org.onosproject.app.vtnrsc.VirtualPort;
39import org.onosproject.app.vtnrsc.VirtualPortId;
40import org.onosproject.app.vtnrsc.tenantnetwork.TenantNetworkService;
41import org.onosproject.app.vtnrsc.virtualport.VirtualPortService;
42import org.onosproject.core.ApplicationId;
43import org.onosproject.core.CoreService;
44import org.onosproject.net.Device;
45import org.onosproject.net.DeviceId;
46import org.onosproject.net.Host;
47import org.onosproject.net.HostId;
48import org.onosproject.net.Port;
49import org.onosproject.net.PortNumber;
50import org.onosproject.net.behaviour.BridgeConfig;
51import org.onosproject.net.behaviour.BridgeName;
52import org.onosproject.net.behaviour.DefaultTunnelDescription;
53import org.onosproject.net.behaviour.IpTunnelEndPoint;
54import org.onosproject.net.behaviour.TunnelConfig;
55import org.onosproject.net.behaviour.TunnelDescription;
56import org.onosproject.net.behaviour.TunnelEndPoint;
57import org.onosproject.net.device.DeviceEvent;
58import org.onosproject.net.device.DeviceListener;
59import org.onosproject.net.device.DeviceService;
60import org.onosproject.net.driver.DriverHandler;
61import org.onosproject.net.driver.DriverService;
62import org.onosproject.net.flow.DefaultTrafficSelector;
63import org.onosproject.net.flow.DefaultTrafficTreatment;
64import org.onosproject.net.flow.FlowRuleService;
65import org.onosproject.net.flow.TrafficSelector;
66import org.onosproject.net.flow.TrafficTreatment;
67import org.onosproject.net.flow.criteria.Criteria;
68import org.onosproject.net.flow.instructions.Instructions;
69import org.onosproject.net.flowobjective.DefaultForwardingObjective;
70import org.onosproject.net.flowobjective.FlowObjectiveService;
71import org.onosproject.net.flowobjective.ForwardingObjective;
72import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
73import org.onosproject.net.flowobjective.Objective;
74import org.onosproject.net.host.HostEvent;
75import org.onosproject.net.host.HostListener;
76import org.onosproject.net.host.HostService;
77import org.onosproject.store.serializers.KryoNamespaces;
78import org.onosproject.store.service.EventuallyConsistentMap;
79import org.onosproject.store.service.StorageService;
80import org.onosproject.store.service.WallClockTimestamp;
81import org.onosproject.vtn.VTNService;
82import org.slf4j.Logger;
83
84/**
85 * Provides implementation of VTNService.
86 */
87@Component(immediate = true)
88@Service
89public class VTNManager implements VTNService {
90 private final Logger log = getLogger(getClass());
91
92 private static final String APP_ID = "org.onosproject.app.vtn";
93 private ScheduledExecutorService backgroundService;
94 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
95 protected DeviceService deviceService;
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected HostService hostService;
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected FlowRuleService flowRuleService;
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected CoreService coreService;
102 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
103 protected StorageService storageService;
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected TenantNetworkService tenantNetworkService;
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected VirtualPortService virtualPortService;
108 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
109 protected DriverService driverService;
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected FlowObjectiveService flowObjectiveService;
112 private EventuallyConsistentMap<HostId, SegmentationId> binding;
113 private ApplicationId appId;
114 private HostListener hostListener = new InnerHostListener();
115 private DeviceListener deviceListener = new InnerDeviceListener();
116 private static final String IFACEID = "ifaceid";
117 private static final String PORT_HEAD = "vxlan";
118 private static final int MAC_TABLE = 40;
119 private static final short ETH_TYPE_MAC = 40;
120 private static final short ETH_TYPE_PORT = 0;
121 private static final String DEFAULT_BRIDGE_NAME = "br-int";
122 private static final String CONTROLLER_IP_KEY = "ipaddress";
123
124 @Activate
125 public void activate() {
126 KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
127 .register(KryoNamespaces.API);
128 appId = coreService.registerApplication(APP_ID);
129 deviceService.addListener(deviceListener);
130 hostService.addListener(hostListener);
131 backgroundService = newSingleThreadScheduledExecutor(groupedThreads("onos-apps/vtn",
132 "manager-background"));
133 binding = storageService
134 .<HostId, SegmentationId>eventuallyConsistentMapBuilder()
135 .withName("all_tunnel").withSerializer(serializer)
136 .withTimestampProvider((k, v) -> new WallClockTimestamp())
137 .build();
138 log.info("Started");
139 }
140
141 @Deactivate
142 public void deactivate() {
143 backgroundService.shutdown();
144 binding.destroy();
145 log.info("Stopped");
146 }
147
148 @Override
149 public void onServerDetected(Device device) {
150 Iterable<Device> devices = deviceService.getAvailableDevices();
151 DriverHandler handler = driverService.createHandler(device.id());
152 TunnelConfig config = handler.behaviour(TunnelConfig.class);
153 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
154 bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE_NAME));
155 String ipAddress = device.annotations().value(CONTROLLER_IP_KEY);
156 IpAddress ip = IpAddress.valueOf(ipAddress);
157 TunnelEndPoint tunnelAsSrcSrc = IpTunnelEndPoint.ipTunnelPoint(ip);
158 TunnelEndPoint tunnelAsDstDst = IpTunnelEndPoint.ipTunnelPoint(ip);
159 devices.forEach(d -> {
160 if (!device.id().equals(d.id())) {
161 String ipAddress1 = d.annotations().value(CONTROLLER_IP_KEY);
162 IpAddress ip1 = IpAddress.valueOf(ipAddress1);
163 TunnelEndPoint tunnelAsSrcDst = IpTunnelEndPoint
164 .ipTunnelPoint(ip1);
165 TunnelEndPoint tunnelAsDstSrc = IpTunnelEndPoint
166 .ipTunnelPoint(ip1);
167 TunnelDescription tunnelAsSrc = new DefaultTunnelDescription(
168 tunnelAsSrcSrc,
169 tunnelAsSrcDst,
170 TunnelDescription.Type.VXLAN,
171 null);
172 TunnelDescription tunnelAsDst = new DefaultTunnelDescription(
173 tunnelAsDstDst,
174 tunnelAsDstSrc,
175 TunnelDescription.Type.VXLAN,
176 null);
177 config.createTunnel(tunnelAsSrc);
178 config.createTunnel(tunnelAsDst);
179 }
180 });
181 }
182
183 @Override
184 public void onServerVanished(Device device) {
185 Iterable<Device> devices = deviceService.getAvailableDevices();
186 DriverHandler handler = driverService.createHandler(device.id());
187 TunnelConfig config = handler.behaviour(TunnelConfig.class);
188 BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class);
189 bridgeConfig.deleteBridge(BridgeName.bridgeName(DEFAULT_BRIDGE_NAME));
190 String ipAddress = device.annotations().value(CONTROLLER_IP_KEY);
191 IpAddress ip = IpAddress.valueOf(ipAddress);
192 TunnelEndPoint tunnelAsSrcSrc = IpTunnelEndPoint.ipTunnelPoint(ip);
193 TunnelEndPoint tunnelAsDstDst = IpTunnelEndPoint.ipTunnelPoint(ip);
194 devices.forEach(d -> {
195 if (!device.id().equals(d.id())) {
196 String ipAddress1 = d.annotations().value(CONTROLLER_IP_KEY);
197 IpAddress ip1 = IpAddress.valueOf(ipAddress1);
198 TunnelEndPoint tunnelAsSrcDst = IpTunnelEndPoint
199 .ipTunnelPoint(ip1);
200 TunnelEndPoint tunnelAsDstSrc = IpTunnelEndPoint
201 .ipTunnelPoint(ip1);
202 TunnelDescription tunnelAsSrc = new DefaultTunnelDescription(
203 tunnelAsSrcSrc,
204 tunnelAsSrcDst,
205 TunnelDescription.Type.VXLAN,
206 null);
207 TunnelDescription tunnelAsDst = new DefaultTunnelDescription(
208 tunnelAsDstDst,
209 tunnelAsDstSrc,
210 TunnelDescription.Type.VXLAN,
211 null);
212 config.removeTunnel(tunnelAsSrc);
213 config.removeTunnel(tunnelAsDst);
214 }
215 });
216 }
217
218 @Override
219 public void onOvsDetected(Device device) {
220 programMacDefaultRules(device.id(), appId, Objective.Operation.ADD);
221 programPortDefaultRules(device.id(), appId, Objective.Operation.ADD);
222 }
223
224 @Override
225 public void onOvsVanished(Device device) {
226 programMacDefaultRules(device.id(), appId, Objective.Operation.REMOVE);
227 programPortDefaultRules(device.id(), appId, Objective.Operation.REMOVE);
228 }
229
230 @Override
231 public void onHostDetected(Host host) {
232 String ifaceId = host.annotations().value(IFACEID);
233 VirtualPortId portId = VirtualPortId.portId(ifaceId);
234 VirtualPort port = virtualPortService.getPort(portId);
235 TenantNetwork network = tenantNetworkService.getNetwork(port
236 .networkId());
237 binding.put(host.id(), network.segmentationId());
238 DeviceId deviceId = host.location().deviceId();
239 List<Port> allPorts = deviceService.getPorts(deviceId);
240 PortNumber inPort = host.location().port();
241 Set<Port> localPorts = new HashSet<>();
242 allPorts.forEach(p -> {
243 if (!p.number().name().startsWith(PORT_HEAD)) {
244 localPorts.add(p);
245 }
246 });
247 programLocalBcastRules(deviceId, network.segmentationId(), inPort,
248 allPorts, appId, Objective.Operation.ADD);
249 programLocalOut(deviceId, network.segmentationId(), inPort, host.mac(),
250 appId, Objective.Operation.ADD);
251 programTunnelFloodOut(deviceId, network.segmentationId(), inPort,
252 localPorts, appId, Objective.Operation.ADD);
253 programTunnelOut(deviceId, network.segmentationId(), inPort,
254 host.mac(), appId, Objective.Operation.ADD);
255 programLocalIn(deviceId, network.segmentationId(), inPort, host.mac(),
256 appId, Objective.Operation.ADD);
257 programTunnelIn(deviceId, network.segmentationId(), inPort, host.mac(),
258 appId, Objective.Operation.ADD);
259 }
260
261 @Override
262 public void onHostVanished(Host host) {
263 SegmentationId segId = binding.remove(host.id());
264 DeviceId deviceId = host.location().deviceId();
265 List<Port> allPorts = deviceService.getPorts(deviceId);
266 PortNumber inPort = host.location().port();
267 Set<Port> localPorts = new HashSet<>();
268 allPorts.forEach(p -> {
269 if (!p.number().name().startsWith(PORT_HEAD)) {
270 localPorts.add(p);
271 }
272 });
273 programLocalBcastRules(deviceId, segId, inPort, allPorts, appId,
274 Objective.Operation.REMOVE);
275 programLocalOut(deviceId, segId, inPort, host.mac(), appId,
276 Objective.Operation.REMOVE);
277 programTunnelFloodOut(deviceId, segId, inPort, localPorts, appId,
278 Objective.Operation.REMOVE);
279 programTunnelOut(deviceId, segId, inPort, host.mac(), appId,
280 Objective.Operation.REMOVE);
281 programLocalIn(deviceId, segId, inPort, host.mac(), appId,
282 Objective.Operation.REMOVE);
283 programTunnelIn(deviceId, segId, inPort, host.mac(), appId,
284 Objective.Operation.REMOVE);
285 }
286
287 private class InnerDeviceListener implements DeviceListener {
288
289 @Override
290 public void event(DeviceEvent event) {
291 Device device = event.subject();
292 if (Device.Type.CONTROLLER == device.type()
293 && DeviceEvent.Type.DEVICE_ADDED == event.type()) {
294 backgroundService.execute(() -> {
295 onServerDetected(device);
296 });
297 } else if (Device.Type.CONTROLLER == device.type()
298 && DeviceEvent.Type.DEVICE_REMOVED == event.type()) {
299 backgroundService.execute(() -> {
300 onServerVanished(device);
301 });
302 } else if (Device.Type.SWITCH == device.type()
303 && DeviceEvent.Type.DEVICE_ADDED == event.type()) {
304 backgroundService.execute(() -> {
305 onOvsDetected(device);
306 });
307 } else if (Device.Type.SWITCH == device.type()
308 && DeviceEvent.Type.DEVICE_REMOVED == event.type()) {
309 backgroundService.execute(() -> {
310 onOvsVanished(device);
311 });
312 } else {
313 log.info("do nothing for this device type");
314 }
315 }
316
317 }
318
319 private class InnerHostListener implements HostListener {
320
321 @Override
322 public void event(HostEvent event) {
323 Host host = event.subject();
324 if (HostEvent.Type.HOST_ADDED == event.type()) {
325 backgroundService.execute(() -> {
326 onHostDetected(host);
327 });
328 } else if (HostEvent.Type.HOST_REMOVED == event.type()) {
329 backgroundService.execute(() -> {
330 onHostVanished(host);
331 });
332 } else {
333 log.info("unknow host");
334 }
335 }
336
337 }
338
339 //Used to forward the flows to the local VM.
340 private void programLocalOut(DeviceId dpid, SegmentationId segmentationId,
341 PortNumber outPort, MacAddress sourceMac,
342 ApplicationId appid, Objective.Operation type) {
343 TrafficSelector selector = DefaultTrafficSelector.builder()
344 .matchEthDst(sourceMac).matchEthType(ETH_TYPE_MAC).build();
345 TrafficTreatment treatment = DefaultTrafficTreatment
346 .builder()
347 .add(Instructions.modTunnelId(Long.parseLong(segmentationId
348 .toString()))).setOutput(outPort).build();
349 ForwardingObjective.Builder objective = DefaultForwardingObjective
350 .builder().withTreatment(treatment).withSelector(selector)
351 .fromApp(appId).withFlag(Flag.SPECIFIC);
352 if (type.equals(Objective.Operation.ADD)) {
353 flowObjectiveService.forward(dpid, objective.add());
354 } else {
355 flowObjectiveService.forward(dpid, objective.remove());
356 }
357
358 }
359
360 //Used to forward the flows to the remote VM via VXLAN tunnel.
361 private void programTunnelOut(DeviceId dpid, SegmentationId segmentationId,
362 PortNumber outPort, MacAddress sourceMac,
363 ApplicationId appid, Objective.Operation type) {
364 TrafficSelector selector = DefaultTrafficSelector.builder()
365 .matchEthDst(sourceMac).matchEthType(ETH_TYPE_MAC).build();
366 TrafficTreatment treatment = DefaultTrafficTreatment
367 .builder()
368 .add(Instructions.modTunnelId(Long.parseLong(segmentationId
369 .toString()))).setOutput(outPort).build();
370 ForwardingObjective.Builder objective = DefaultForwardingObjective
371 .builder().withTreatment(treatment).withSelector(selector)
372 .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC);
373 if (type.equals(Objective.Operation.ADD)) {
374 flowObjectiveService.forward(dpid, objective.add());
375 } else {
376 flowObjectiveService.forward(dpid, objective.remove());
377 }
378 }
379
380 //Used to forward multicast flows to remote VMs of the same tenant via VXLAN tunnel.
381 private void programTunnelFloodOut(DeviceId dpid,
382 SegmentationId segmentationId,
383 PortNumber ofPortOut,
384 Iterable<Port> localports,
385 ApplicationId appid,
386 Objective.Operation type) {
387 TrafficSelector selector = DefaultTrafficSelector
388 .builder()
389 .matchInPort(ofPortOut)
390 .matchEthType(ETH_TYPE_MAC)
391 .add(Criteria.matchTunnelId(Long.parseLong(segmentationId
392 .toString()))).matchEthDst(MacAddress.BROADCAST)
393 .build();
394 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
395
396 for (Port outport : localports) {
397 treatment.setOutput(outport.number());
398 }
399
400 ForwardingObjective.Builder objective = DefaultForwardingObjective
401 .builder().withTreatment(treatment.build())
402 .withSelector(selector).fromApp(appId)
403
404 .makePermanent().withFlag(Flag.SPECIFIC);
405 if (type.equals(Objective.Operation.ADD)) {
406 flowObjectiveService.forward(dpid, objective.add());
407 } else {
408 flowObjectiveService.forward(dpid, objective.remove());
409 }
410 }
411
412 //Applies default flows to mac table.
413 private void programMacDefaultRules(DeviceId dpid, ApplicationId appid,
414 Objective.Operation type) {
415 TrafficSelector selector = DefaultTrafficSelector.builder()
416 .matchEthType(ETH_TYPE_MAC).build();
417 TrafficTreatment treatment = DefaultTrafficTreatment.builder().drop()
418 .build();
419
420 ForwardingObjective.Builder objective = DefaultForwardingObjective
421 .builder().withTreatment(treatment).withSelector(selector)
422 .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC);
423 if (type.equals(Objective.Operation.ADD)) {
424 flowObjectiveService.forward(dpid, objective.add());
425 } else {
426 flowObjectiveService.forward(dpid, objective.remove());
427 }
428 }
429
430 //Used to forward the flows to the local VMs with the same tenant.
431 private void programLocalBcastRules(DeviceId dpid,
432 SegmentationId segmentationId,
433 PortNumber inPort, List<Port> allports,
434 ApplicationId appid,
435 Objective.Operation type) {
436 TrafficSelector selector = DefaultTrafficSelector
437 .builder()
438 .matchInPort(inPort)
439 .matchEthType(ETH_TYPE_MAC)
440 .matchEthDst(MacAddress.BROADCAST)
441 .add(Criteria.matchTunnelId(Long.parseLong(segmentationId
442 .toString()))).build();
443 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
444
445 for (Port outport : allports) {
446 if (inPort != outport.number()) {
447 treatment.setOutput(outport.number());
448 }
449 }
450 ForwardingObjective.Builder objective = DefaultForwardingObjective
451 .builder().withTreatment(treatment.build())
452 .withSelector(selector).fromApp(appId).makePermanent()
453 .withFlag(Flag.SPECIFIC);
454 if (type.equals(Objective.Operation.ADD)) {
455 flowObjectiveService.forward(dpid, objective.add());
456 } else {
457 flowObjectiveService.forward(dpid, objective.remove());
458 }
459 }
460
461 //Used to apply local entry flow.
462 private void programLocalIn(DeviceId dpid, SegmentationId segmentationId,
463 PortNumber inPort, MacAddress srcMac,
464 ApplicationId appid, Objective.Operation type) {
465 TrafficSelector selector = DefaultTrafficSelector.builder()
466 .matchInPort(inPort).matchEthSrc(srcMac)
467 .matchEthType(ETH_TYPE_PORT).build();
468 TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
469
470 treatment.add(Instructions.modTunnelId(Long.parseLong(segmentationId
471 .toString())));
472 treatment.transition(MAC_TABLE);
473 ForwardingObjective.Builder objective = DefaultForwardingObjective
474 .builder().withTreatment(treatment.build())
475 .withSelector(selector).fromApp(appId).makePermanent()
476 .withFlag(Flag.SPECIFIC);
477 if (type.equals(Objective.Operation.ADD)) {
478 flowObjectiveService.forward(dpid, objective.add());
479 } else {
480 flowObjectiveService.forward(dpid, objective.remove());
481 }
482 }
483
484 //Used to forward the flows from the egress tunnel to the VM.
485 private void programTunnelIn(DeviceId dpid, SegmentationId segmentationId,
486 PortNumber inPort, MacAddress sourceMac,
487 ApplicationId appid, Objective.Operation type) {
488 TrafficSelector selector = DefaultTrafficSelector
489 .builder()
490 .matchInPort(inPort)
491 .matchEthType(ETH_TYPE_PORT)
492 .add(Criteria.matchTunnelId(Long.parseLong(segmentationId
493 .toString()))).build();
494 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
495 .transition(MAC_TABLE).build();
496
497 ForwardingObjective.Builder objective = DefaultForwardingObjective
498 .builder().withTreatment(treatment).withSelector(selector)
499 .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC);
500 if (type.equals(Objective.Operation.ADD)) {
501 flowObjectiveService.forward(dpid, objective.add());
502 } else {
503 flowObjectiveService.forward(dpid, objective.remove());
504 }
505 }
506
507 //Applies the default flows to port table.
508 private void programPortDefaultRules(DeviceId dpid, ApplicationId appid,
509 Objective.Operation type) {
510 TrafficSelector selector = DefaultTrafficSelector.builder()
511 .matchEthType(ETH_TYPE_PORT).build();
512 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
513 .transition(MAC_TABLE).build();
514 ForwardingObjective.Builder objective = DefaultForwardingObjective
515 .builder().withTreatment(treatment).withSelector(selector)
516 .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC);
517 if (type.equals(Objective.Operation.ADD)) {
518 flowObjectiveService.forward(dpid, objective.add());
519 } else {
520 flowObjectiveService.forward(dpid, objective.remove());
521 }
522 }
523}