blob: 16c818795e08ef8ca1166c5fc562a54306b46351 [file] [log] [blame]
yoonseon5a51ef72016-10-26 14:50:09 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
yoonseon5a51ef72016-10-26 14:50:09 -07003 *
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
Yoonseon Hanc8089db2017-03-22 20:22:12 +090019import com.google.common.collect.Sets;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070020import org.osgi.service.component.annotations.Activate;
21import org.osgi.service.component.annotations.Component;
22import org.osgi.service.component.annotations.Deactivate;
23import org.osgi.service.component.annotations.Modified;
24import org.osgi.service.component.annotations.Reference;
25import org.osgi.service.component.annotations.ReferenceCardinality;
yoonseon5a51ef72016-10-26 14:50:09 -070026import org.onlab.packet.Ethernet;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.incubator.net.virtual.NetworkId;
30import org.onosproject.incubator.net.virtual.TenantId;
31import org.onosproject.incubator.net.virtual.VirtualDevice;
32import org.onosproject.incubator.net.virtual.VirtualNetwork;
33import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
Harold Huange86d35f2017-06-14 15:15:18 +080034import org.onosproject.incubator.net.virtual.VirtualNetworkEvent;
35import org.onosproject.incubator.net.virtual.VirtualNetworkListener;
Harold Huangbee92f62017-06-07 22:39:37 +080036import org.onosproject.incubator.net.virtual.VirtualPacketContext;
yoonseon5a51ef72016-10-26 14:50:09 -070037import org.onosproject.incubator.net.virtual.VirtualPort;
38import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
39import org.onosproject.incubator.net.virtual.provider.VirtualPacketProvider;
40import org.onosproject.incubator.net.virtual.provider.VirtualPacketProviderService;
yoonseon736a73b2017-01-26 14:27:48 -080041import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
yoonseon5a51ef72016-10-26 14:50:09 -070042import org.onosproject.net.ConnectPoint;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090043import org.onosproject.net.DeviceId;
yoonseon5a51ef72016-10-26 14:50:09 -070044import org.onosproject.net.PortNumber;
45import org.onosproject.net.flow.DefaultTrafficTreatment;
46import org.onosproject.net.flow.TrafficTreatment;
47import org.onosproject.net.flow.instructions.Instruction;
48import org.onosproject.net.flow.instructions.Instructions;
49import org.onosproject.net.packet.DefaultInboundPacket;
50import org.onosproject.net.packet.DefaultOutboundPacket;
51import org.onosproject.net.packet.InboundPacket;
52import org.onosproject.net.packet.OutboundPacket;
53import org.onosproject.net.packet.PacketContext;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090054import org.onosproject.net.packet.PacketPriority;
yoonseon5a51ef72016-10-26 14:50:09 -070055import org.onosproject.net.packet.PacketProcessor;
56import org.onosproject.net.packet.PacketService;
57import org.onosproject.net.provider.ProviderId;
58import org.osgi.service.component.ComponentContext;
59import org.slf4j.Logger;
60
61import java.nio.ByteBuffer;
62import java.util.Dictionary;
63import java.util.HashSet;
yoonseon5a51ef72016-10-26 14:50:09 -070064import java.util.Optional;
65import java.util.Set;
66import java.util.stream.Collectors;
67
68import static org.slf4j.LoggerFactory.getLogger;
69
Ray Milkeyd84f89b2018-08-17 14:54:17 -070070@Component(service = VirtualPacketProvider.class)
yoonseon5a51ef72016-10-26 14:50:09 -070071public class DefaultVirtualPacketProvider extends AbstractVirtualProvider
72 implements VirtualPacketProvider {
73
74 private static final int PACKET_PROCESSOR_PRIORITY = 1;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090075 private static final PacketPriority VIRTUAL_PACKET_PRIORITY = PacketPriority.REACTIVE;
yoonseon5a51ef72016-10-26 14:50:09 -070076
77 private final Logger log = getLogger(getClass());
78
Ray Milkeyd84f89b2018-08-17 14:54:17 -070079 @Reference(cardinality = ReferenceCardinality.MANDATORY)
yoonseon5a51ef72016-10-26 14:50:09 -070080 protected PacketService packetService;
81
Ray Milkeyd84f89b2018-08-17 14:54:17 -070082 @Reference(cardinality = ReferenceCardinality.MANDATORY)
yoonseon5a51ef72016-10-26 14:50:09 -070083 protected CoreService coreService;
84
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yoonseon Hanc8089db2017-03-22 20:22:12 +090086 protected VirtualNetworkAdminService vnaService;
yoonseon5a51ef72016-10-26 14:50:09 -070087
Ray Milkeyd84f89b2018-08-17 14:54:17 -070088 @Reference(cardinality = ReferenceCardinality.MANDATORY)
yoonseon736a73b2017-01-26 14:27:48 -080089 protected VirtualProviderRegistryService providerRegistryService;
yoonseon5a51ef72016-10-26 14:50:09 -070090
Harold Huange86d35f2017-06-14 15:15:18 +080091 private final VirtualNetworkListener virtualNetListener = new InternalVirtualNetworkListener();
Yoonseon Hanc8089db2017-03-22 20:22:12 +090092
Harold Huange86d35f2017-06-14 15:15:18 +080093 private InternalPacketProcessor processor = null;
94
95 private Set<NetworkId> networkIdSet = Sets.newConcurrentHashSet();
96
97 private ApplicationId appId;
yoonseon5a51ef72016-10-26 14:50:09 -070098
99 /**
100 * Creates a provider with the supplied identifier.
yoonseon5a51ef72016-10-26 14:50:09 -0700101 */
102 public DefaultVirtualPacketProvider() {
Thomas Vachuska11b99fc2017-04-27 12:51:04 -0700103 super(new ProviderId("virtual-packet", "org.onosproject.virtual.virtual-packet"));
yoonseon5a51ef72016-10-26 14:50:09 -0700104 }
105
106 @Activate
107 public void activate() {
Thomas Vachuska11b99fc2017-04-27 12:51:04 -0700108 appId = coreService.registerApplication("org.onosproject.virtual.virtual-packet");
yoonseon736a73b2017-01-26 14:27:48 -0800109 providerRegistryService.registerProvider(this);
Harold Huange86d35f2017-06-14 15:15:18 +0800110 vnaService.addListener(virtualNetListener);
yoonseon5a51ef72016-10-26 14:50:09 -0700111
yoonseon5a51ef72016-10-26 14:50:09 -0700112 log.info("Started");
113 }
114
115 @Deactivate
116 public void deactivate() {
Harold Huange86d35f2017-06-14 15:15:18 +0800117
Thomas Vachuska11b99fc2017-04-27 12:51:04 -0700118 providerRegistryService.unregisterProvider(this);
Harold Huange86d35f2017-06-14 15:15:18 +0800119 vnaService.removeListener(virtualNetListener);
120
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900121 log.info("Stopped");
yoonseon5a51ef72016-10-26 14:50:09 -0700122 }
123
124 @Modified
125 protected void modified(ComponentContext context) {
126 Dictionary<?, ?> properties = context.getProperties();
127 }
128
129
130 @Override
131 public void emit(NetworkId networkId, OutboundPacket packet) {
Harold Huangbee92f62017-06-07 22:39:37 +0800132 devirtualize(networkId, packet)
133 .forEach(outboundPacket -> packetService.emit(outboundPacket));
yoonseon5a51ef72016-10-26 14:50:09 -0700134 }
135
Harold Huange86d35f2017-06-14 15:15:18 +0800136 /**
137 * Just for test.
138 */
139 protected void startPacketHandling() {
140 processor = new InternalPacketProcessor();
141 packetService.addProcessor(processor, PACKET_PROCESSOR_PRIORITY);
yoonseonfb4a1db2017-01-31 11:38:30 -0800142 }
143
yoonseon5a51ef72016-10-26 14:50:09 -0700144 /**
Harold Huangbee92f62017-06-07 22:39:37 +0800145 * Send the outbound packet of a virtual context.
146 * This method is designed to support Context's send() method that invoked
147 * by applications.
148 * See {@link org.onosproject.net.packet.PacketContext}
149 *
150 * @param virtualPacketContext virtual packet context
151 */
152 protected void send(VirtualPacketContext virtualPacketContext) {
153 devirtualizeContext(virtualPacketContext)
154 .forEach(outboundPacket -> packetService.emit(outboundPacket));
155 }
156
157 /**
yoonseon5a51ef72016-10-26 14:50:09 -0700158 * Translate the requested physical PacketContext into a virtual PacketContext.
Harold Huangbee92f62017-06-07 22:39:37 +0800159 * See {@link org.onosproject.net.packet.PacketContext}
yoonseon5a51ef72016-10-26 14:50:09 -0700160 *
161 * @param context A physical PacketContext be translated
162 * @return A translated virtual PacketContext
163 */
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900164 private VirtualPacketContext virtualize(PacketContext context) {
yoonseon5a51ef72016-10-26 14:50:09 -0700165
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900166 VirtualPort vPort = getMappedVirtualPort(context.inPacket().receivedFrom());
yoonseon5a51ef72016-10-26 14:50:09 -0700167
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900168 if (vPort != null) {
yoonseon5a51ef72016-10-26 14:50:09 -0700169 ConnectPoint cp = new ConnectPoint(vPort.element().id(),
170 vPort.number());
171
172 Ethernet eth = context.inPacket().parsed();
173 eth.setVlanID(Ethernet.VLAN_UNTAGGED);
174
175 InboundPacket inPacket =
176 new DefaultInboundPacket(cp, eth,
177 ByteBuffer.wrap(eth.serialize()));
178
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900179 DefaultOutboundPacket outPkt =
180 new DefaultOutboundPacket(cp.deviceId(),
181 DefaultTrafficTreatment.builder().build(),
182 ByteBuffer.wrap(eth.serialize()));
183
yoonseon5a51ef72016-10-26 14:50:09 -0700184 VirtualPacketContext vContext =
Harold Huangbee92f62017-06-07 22:39:37 +0800185 new DefaultVirtualPacketContext(context.time(), inPacket, outPkt,
Harold Huange86d35f2017-06-14 15:15:18 +0800186 false, vPort.networkId(),
yoonseon5a51ef72016-10-26 14:50:09 -0700187 this);
188
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900189 return vContext;
190 } else {
191 return null;
yoonseon5a51ef72016-10-26 14:50:09 -0700192 }
193
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900194 }
195
196 /**
197 * Find the corresponding virtual port with the physical port.
198 *
199 * @param cp the connect point for the physical network
200 * @return a virtual port
201 */
202 private VirtualPort getMappedVirtualPort(ConnectPoint cp) {
203 Set<TenantId> tIds = vnaService.getTenantIds();
204
205 Set<VirtualNetwork> vNetworks = new HashSet<>();
206 tIds.forEach(tid -> vNetworks.addAll(vnaService.getVirtualNetworks(tid)));
207
208 for (VirtualNetwork vNet : vNetworks) {
209 Set<VirtualDevice> vDevices = vnaService.getVirtualDevices(vNet.id());
210
211 Set<VirtualPort> vPorts = new HashSet<>();
212 vDevices.forEach(dev -> vPorts
213 .addAll(vnaService.getVirtualPorts(dev.networkId(), dev.id())));
214
215 VirtualPort vPort = vPorts.stream()
216 .filter(vp -> vp.realizedBy().equals(cp))
217 .findFirst().orElse(null);
218
219 if (vPort != null) {
220 return vPort;
221 }
222 }
223
224 return null;
yoonseon5a51ef72016-10-26 14:50:09 -0700225 }
226
227 /**
Harold Huangbee92f62017-06-07 22:39:37 +0800228 * Translate the requested virtual outbound packet into
229 * a set of physical OutboundPacket.
230 * See {@link org.onosproject.net.packet.OutboundPacket}
yoonseon5a51ef72016-10-26 14:50:09 -0700231 *
Harold Huangbee92f62017-06-07 22:39:37 +0800232 * @param packet an OutboundPacket to be translated
233 * @return a set of de-virtualized (physical) OutboundPacket
yoonseon5a51ef72016-10-26 14:50:09 -0700234 */
Harold Huangbee92f62017-06-07 22:39:37 +0800235 private Set<OutboundPacket> devirtualize(NetworkId networkId, OutboundPacket packet) {
236 Set<OutboundPacket> outboundPackets = new HashSet<>();
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900237 Set<VirtualPort> vPorts = vnaService
yoonseon5a51ef72016-10-26 14:50:09 -0700238 .getVirtualPorts(networkId, packet.sendThrough());
239
yoonseon5a51ef72016-10-26 14:50:09 -0700240 TrafficTreatment.Builder commonTreatmentBuilder
241 = DefaultTrafficTreatment.builder();
242 packet.treatment().allInstructions().stream()
243 .filter(i -> i.type() != Instruction.Type.OUTPUT)
244 .forEach(i -> commonTreatmentBuilder.add(i));
245 TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
246
Harold Huangbee92f62017-06-07 22:39:37 +0800247 PortNumber vOutPortNum = packet.treatment().allInstructions().stream()
248 .filter(i -> i.type() == Instruction.Type.OUTPUT)
249 .map(i -> ((Instructions.OutputInstruction) i).port())
250 .findFirst().get();
yoonseon5a51ef72016-10-26 14:50:09 -0700251
Harold Huangbee92f62017-06-07 22:39:37 +0800252 if (!vOutPortNum.isLogical()) {
253 Optional<ConnectPoint> optionalCpOut = vPorts.stream()
254 .filter(v -> v.number().equals(vOutPortNum))
255 .map(v -> v.realizedBy())
256 .findFirst();
257 if (!optionalCpOut.isPresent()) {
258 log.warn("Port {} is not realized yet, in Network {}, Device {}",
259 vOutPortNum, networkId, packet.sendThrough());
260 return outboundPackets;
261 }
262 ConnectPoint egressPoint = optionalCpOut.get();
263
264 TrafficTreatment treatment = DefaultTrafficTreatment
265 .builder(commonTreatment)
266 .setOutput(egressPoint.port()).build();
267
268 OutboundPacket outboundPacket = new DefaultOutboundPacket(
269 egressPoint.deviceId(), treatment, packet.data());
270 outboundPackets.add(outboundPacket);
271 } else {
272 if (vOutPortNum == PortNumber.FLOOD) {
273 for (VirtualPort outPort : vPorts) {
274 ConnectPoint cpOut = outPort.realizedBy();
275 if (cpOut != null) {
276 TrafficTreatment treatment = DefaultTrafficTreatment
277 .builder(commonTreatment)
278 .setOutput(cpOut.port()).build();
279 OutboundPacket outboundPacket = new DefaultOutboundPacket(
280 cpOut.deviceId(), treatment, packet.data());
281 outboundPackets.add(outboundPacket);
282 } else {
283 log.warn("Port {} is not realized yet, in Network {}, Device {}",
284 outPort.number(), networkId, packet.sendThrough());
285 }
286 }
287 }
288 }
289
290 return outboundPackets;
yoonseon5a51ef72016-10-26 14:50:09 -0700291 }
292
293 /**
Harold Huangbee92f62017-06-07 22:39:37 +0800294 * Translate the requested virtual packet context into
295 * a set of physical outbound packets.
yoonseon5a51ef72016-10-26 14:50:09 -0700296 *
Harold Huangbee92f62017-06-07 22:39:37 +0800297 * @param context A handled virtual packet context
yoonseon5a51ef72016-10-26 14:50:09 -0700298 */
Harold Huangbee92f62017-06-07 22:39:37 +0800299 private Set<OutboundPacket> devirtualizeContext(VirtualPacketContext context) {
yoonseon5a51ef72016-10-26 14:50:09 -0700300
Harold Huangbee92f62017-06-07 22:39:37 +0800301 Set<OutboundPacket> outboundPackets = new HashSet<>();
302
303 NetworkId networkId = context.networkId();
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900304 TrafficTreatment vTreatment = context.treatmentBuilder().build();
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900305 DeviceId sendThrough = context.outPacket().sendThrough();
yoonseon5a51ef72016-10-26 14:50:09 -0700306
Harold Huangbee92f62017-06-07 22:39:37 +0800307 Set<VirtualPort> vPorts = vnaService
308 .getVirtualPorts(networkId, sendThrough);
309
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900310 PortNumber vOutPortNum = vTreatment.allInstructions().stream()
311 .filter(i -> i.type() == Instruction.Type.OUTPUT)
312 .map(i -> ((Instructions.OutputInstruction) i).port())
313 .findFirst().get();
314
315 TrafficTreatment.Builder commonTreatmentBuilder
316 = DefaultTrafficTreatment.builder();
317 vTreatment.allInstructions().stream()
318 .filter(i -> i.type() != Instruction.Type.OUTPUT)
319 .forEach(i -> commonTreatmentBuilder.add(i));
320 TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
321
322 if (!vOutPortNum.isLogical()) {
Harold Huangbee92f62017-06-07 22:39:37 +0800323 Optional<ConnectPoint> optionalCpOut = vPorts.stream()
324 .filter(v -> v.number().equals(vOutPortNum))
325 .map(v -> v.realizedBy())
326 .findFirst();
327 if (!optionalCpOut.isPresent()) {
328 log.warn("Port {} is not realized yet, in Network {}, Device {}",
329 vOutPortNum, networkId, sendThrough);
330 return outboundPackets;
331 }
332 ConnectPoint egressPoint = optionalCpOut.get();
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900333
Harold Huangbee92f62017-06-07 22:39:37 +0800334 TrafficTreatment treatment = DefaultTrafficTreatment
335 .builder(commonTreatment)
336 .setOutput(egressPoint.port()).build();
337
338 OutboundPacket outboundPacket = new DefaultOutboundPacket(
339 egressPoint.deviceId(), treatment, context.outPacket().data());
340 outboundPackets.add(outboundPacket);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900341 } else {
342 if (vOutPortNum == PortNumber.FLOOD) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900343 Set<VirtualPort> outPorts = vPorts.stream()
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700344 .filter(vp -> !vp.number().isLogical())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900345 .filter(vp -> vp.number() !=
346 context.inPacket().receivedFrom().port())
347 .collect(Collectors.toSet());
348
349 for (VirtualPort outPort : outPorts) {
Harold Huangbee92f62017-06-07 22:39:37 +0800350 ConnectPoint cpOut = outPort.realizedBy();
351 if (cpOut != null) {
352 TrafficTreatment treatment = DefaultTrafficTreatment
353 .builder(commonTreatment)
354 .setOutput(cpOut.port()).build();
355 OutboundPacket outboundPacket = new DefaultOutboundPacket(
356 cpOut.deviceId(), treatment, context.outPacket().data());
357 outboundPackets.add(outboundPacket);
358 } else {
359 log.warn("Port {} is not realized yet, in Network {}, Device {}",
360 outPort.number(), networkId, sendThrough);
361 }
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900362 }
363 }
yoonseon5a51ef72016-10-26 14:50:09 -0700364 }
Harold Huangbee92f62017-06-07 22:39:37 +0800365 return outboundPackets;
yoonseon5a51ef72016-10-26 14:50:09 -0700366 }
367
368 private final class InternalPacketProcessor implements PacketProcessor {
369
370 @Override
371 public void process(PacketContext context) {
Harold Huange86d35f2017-06-14 15:15:18 +0800372 if (context.isHandled()) {
373 return;
374 }
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900375 VirtualPacketContext vContexts = virtualize(context);
yoonseon736a73b2017-01-26 14:27:48 -0800376
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900377 if (vContexts == null) {
378 return;
379 }
380
381 VirtualPacketProviderService service =
382 (VirtualPacketProviderService) providerRegistryService
Harold Huangbee92f62017-06-07 22:39:37 +0800383 .getProviderService(vContexts.networkId(),
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900384 VirtualPacketProvider.class);
385 if (service != null) {
386 service.processPacket(vContexts);
387 }
yoonseon5a51ef72016-10-26 14:50:09 -0700388 }
389 }
Harold Huange86d35f2017-06-14 15:15:18 +0800390
391 private class InternalVirtualNetworkListener implements VirtualNetworkListener {
392
393 @Override
394 public void event(VirtualNetworkEvent event) {
395 switch (event.type()) {
396 case NETWORK_ADDED:
397 if (networkIdSet.isEmpty()) {
398 processor = new InternalPacketProcessor();
399 packetService.addProcessor(processor, PACKET_PROCESSOR_PRIORITY);
400 log.info("Packet processor {} for virtual network is added.", processor.getClass().getName());
401 }
402 networkIdSet.add(event.subject());
403 break;
404
405 case NETWORK_REMOVED:
406 networkIdSet.remove(event.subject());
407 if (networkIdSet.isEmpty()) {
408 packetService.removeProcessor(processor);
409 log.info("Packet processor {} for virtual network is removed.", processor.getClass().getName());
410 processor = null;
411 }
412 break;
413
414 default:
415 // do nothing
416 break;
417 }
418 }
419 }
420
yoonseon5a51ef72016-10-26 14:50:09 -0700421}