blob: 63c3cc24efddf56179dee4e18c83015353315a1f [file] [log] [blame]
yoonseon5a51ef72016-10-26 14:50:09 -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.Maps;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090020import com.google.common.collect.Sets;
yoonseon5a51ef72016-10-26 14:50:09 -070021import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
24import org.apache.felix.scr.annotations.Modified;
25import org.apache.felix.scr.annotations.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.Ethernet;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.incubator.net.virtual.NetworkId;
31import org.onosproject.incubator.net.virtual.TenantId;
32import org.onosproject.incubator.net.virtual.VirtualDevice;
33import org.onosproject.incubator.net.virtual.VirtualNetwork;
34import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService;
35import org.onosproject.incubator.net.virtual.VirtualPort;
36import org.onosproject.incubator.net.virtual.provider.AbstractVirtualProvider;
37import org.onosproject.incubator.net.virtual.provider.VirtualPacketProvider;
38import org.onosproject.incubator.net.virtual.provider.VirtualPacketProviderService;
yoonseon736a73b2017-01-26 14:27:48 -080039import org.onosproject.incubator.net.virtual.provider.VirtualProviderRegistryService;
yoonseon5a51ef72016-10-26 14:50:09 -070040import org.onosproject.net.ConnectPoint;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090041import org.onosproject.net.DeviceId;
yoonseon5a51ef72016-10-26 14:50:09 -070042import org.onosproject.net.PortNumber;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.TrafficTreatment;
45import org.onosproject.net.flow.instructions.Instruction;
46import org.onosproject.net.flow.instructions.Instructions;
47import org.onosproject.net.packet.DefaultInboundPacket;
48import org.onosproject.net.packet.DefaultOutboundPacket;
49import org.onosproject.net.packet.InboundPacket;
50import org.onosproject.net.packet.OutboundPacket;
51import org.onosproject.net.packet.PacketContext;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090052import org.onosproject.net.packet.PacketPriority;
yoonseon5a51ef72016-10-26 14:50:09 -070053import org.onosproject.net.packet.PacketProcessor;
54import org.onosproject.net.packet.PacketService;
55import org.onosproject.net.provider.ProviderId;
56import org.osgi.service.component.ComponentContext;
57import org.slf4j.Logger;
58
59import java.nio.ByteBuffer;
60import java.util.Dictionary;
61import java.util.HashSet;
62import java.util.Map;
63import java.util.Optional;
64import java.util.Set;
65import java.util.stream.Collectors;
66
67import static org.slf4j.LoggerFactory.getLogger;
68
69@Component(immediate = true)
70public class DefaultVirtualPacketProvider extends AbstractVirtualProvider
71 implements VirtualPacketProvider {
72
73 private static final int PACKET_PROCESSOR_PRIORITY = 1;
Yoonseon Hanc8089db2017-03-22 20:22:12 +090074 private static final PacketPriority VIRTUAL_PACKET_PRIORITY = PacketPriority.REACTIVE;
yoonseon5a51ef72016-10-26 14:50:09 -070075
76 private final Logger log = getLogger(getClass());
77
78 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
yoonseon5a51ef72016-10-26 14:50:09 -070079 protected PacketService packetService;
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected CoreService coreService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Yoonseon Hanc8089db2017-03-22 20:22:12 +090085 protected VirtualNetworkAdminService vnaService;
yoonseon5a51ef72016-10-26 14:50:09 -070086
yoonseon736a73b2017-01-26 14:27:48 -080087 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected VirtualProviderRegistryService providerRegistryService;
yoonseon5a51ef72016-10-26 14:50:09 -070089
90 ApplicationId appId;
91 InternalPacketProcessor processor;
92
Yoonseon Hanc8089db2017-03-22 20:22:12 +090093 private Map<VirtualPacketContext, PacketContext> contextMap;
94
95 private Set<NetworkId> requestsSet = Sets.newHashSet();
yoonseon5a51ef72016-10-26 14:50:09 -070096
97 /**
98 * Creates a provider with the supplied identifier.
99 *
100 */
101 public DefaultVirtualPacketProvider() {
102 super(new ProviderId("virtual-packet",
103 "org.onosproject.virtual.virtual-packet"));
104 }
105
106 @Activate
107 public void activate() {
108 appId = coreService.registerApplication(
yoonseon4994c3d2017-01-27 13:07:18 -0800109 "org.onosproject.virtual.virtual-packet");
yoonseon736a73b2017-01-26 14:27:48 -0800110 providerRegistryService.registerProvider(this);
yoonseon5a51ef72016-10-26 14:50:09 -0700111
yoonseon5a51ef72016-10-26 14:50:09 -0700112 contextMap = Maps.newConcurrentMap();
113
114 log.info("Started");
115 }
116
117 @Deactivate
118 public void deactivate() {
Jonathan Hart66028672017-02-02 16:25:49 -0800119 if (processor != null) {
120 packetService.removeProcessor(processor);
121 }
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900122 log.info("Stopped");
yoonseon5a51ef72016-10-26 14:50:09 -0700123 }
124
125 @Modified
126 protected void modified(ComponentContext context) {
127 Dictionary<?, ?> properties = context.getProperties();
128 }
129
130
131 @Override
132 public void emit(NetworkId networkId, OutboundPacket packet) {
133 packetService.emit(devirtualize(networkId, packet));
134 }
135
yoonseonfb4a1db2017-01-31 11:38:30 -0800136 @Override
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900137 public void startPacketHandling(NetworkId networkId) {
138 requestsSet.add(networkId);
139
140 if (processor == null) {
141 processor = new InternalPacketProcessor();
142 packetService.addProcessor(processor, PACKET_PROCESSOR_PRIORITY);
143 }
144 }
145
146 @Override
147 public void stopPacketHandling(NetworkId networkId) {
148 requestsSet.remove(networkId);
149
150 if (requestsSet.isEmpty()) {
151 packetService.removeProcessor(processor);
152 processor = null;
153 }
yoonseonfb4a1db2017-01-31 11:38:30 -0800154 }
155
yoonseon5a51ef72016-10-26 14:50:09 -0700156 /**
157 * Translate the requested physical PacketContext into a virtual PacketContext.
158 * See {@link org.onosproject.net.packet.OutboundPacket}
159 *
160 * @param context A physical PacketContext be translated
161 * @return A translated virtual PacketContext
162 */
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900163 private VirtualPacketContext virtualize(PacketContext context) {
yoonseon5a51ef72016-10-26 14:50:09 -0700164
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900165 VirtualPort vPort = getMappedVirtualPort(context.inPacket().receivedFrom());
yoonseon5a51ef72016-10-26 14:50:09 -0700166
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900167 if (vPort != null) {
yoonseon5a51ef72016-10-26 14:50:09 -0700168 ConnectPoint cp = new ConnectPoint(vPort.element().id(),
169 vPort.number());
170
171 Ethernet eth = context.inPacket().parsed();
172 eth.setVlanID(Ethernet.VLAN_UNTAGGED);
173
174 InboundPacket inPacket =
175 new DefaultInboundPacket(cp, eth,
176 ByteBuffer.wrap(eth.serialize()));
177
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900178 DefaultOutboundPacket outPkt =
179 new DefaultOutboundPacket(cp.deviceId(),
180 DefaultTrafficTreatment.builder().build(),
181 ByteBuffer.wrap(eth.serialize()));
182
yoonseon5a51ef72016-10-26 14:50:09 -0700183 VirtualPacketContext vContext =
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900184 new VirtualPacketContext(context.time(), inPacket, outPkt,
yoonseon5a51ef72016-10-26 14:50:09 -0700185 false, vPort.networkId(),
186 this);
187
188 contextMap.put(vContext, context);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900189
190 return vContext;
191 } else {
192 return null;
yoonseon5a51ef72016-10-26 14:50:09 -0700193 }
194
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900195 }
196
197 /**
198 * Find the corresponding virtual port with the physical port.
199 *
200 * @param cp the connect point for the physical network
201 * @return a virtual port
202 */
203 private VirtualPort getMappedVirtualPort(ConnectPoint cp) {
204 Set<TenantId> tIds = vnaService.getTenantIds();
205
206 Set<VirtualNetwork> vNetworks = new HashSet<>();
207 tIds.forEach(tid -> vNetworks.addAll(vnaService.getVirtualNetworks(tid)));
208
209 for (VirtualNetwork vNet : vNetworks) {
210 Set<VirtualDevice> vDevices = vnaService.getVirtualDevices(vNet.id());
211
212 Set<VirtualPort> vPorts = new HashSet<>();
213 vDevices.forEach(dev -> vPorts
214 .addAll(vnaService.getVirtualPorts(dev.networkId(), dev.id())));
215
216 VirtualPort vPort = vPorts.stream()
217 .filter(vp -> vp.realizedBy().equals(cp))
218 .findFirst().orElse(null);
219
220 if (vPort != null) {
221 return vPort;
222 }
223 }
224
225 return null;
yoonseon5a51ef72016-10-26 14:50:09 -0700226 }
227
228 /**
229 * Translate the requested a virtual outbound packet into
230 * a physical OutboundPacket.
231 * See {@link org.onosproject.net.packet.PacketContext}
232 *
233 * @param packet A OutboundPacket to be translated
234 * @return de-virtualized (physical) OutboundPacket
235 */
236 private OutboundPacket devirtualize(NetworkId networkId, OutboundPacket packet) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900237 Set<VirtualPort> vPorts = vnaService
yoonseon5a51ef72016-10-26 14:50:09 -0700238 .getVirtualPorts(networkId, packet.sendThrough());
239
240 PortNumber vOutPortNum = packet.treatment().allInstructions().stream()
241 .filter(i -> i.type() == Instruction.Type.OUTPUT)
242 .map(i -> ((Instructions.OutputInstruction) i).port())
243 .findFirst().get();
244
245 Optional<ConnectPoint> optionalCpOut = vPorts.stream()
246 .filter(v -> v.number().equals(vOutPortNum))
247 .map(v -> v.realizedBy())
248 .findFirst();
249 if (!optionalCpOut.isPresent()) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900250 log.warn("Port {} is not realized yet, in Network {}, Device {}",
yoonseon5a51ef72016-10-26 14:50:09 -0700251 vOutPortNum, networkId, packet.sendThrough());
252 return null;
253 }
254 ConnectPoint egressPoint = optionalCpOut.get();
255
256 TrafficTreatment.Builder commonTreatmentBuilder
257 = DefaultTrafficTreatment.builder();
258 packet.treatment().allInstructions().stream()
259 .filter(i -> i.type() != Instruction.Type.OUTPUT)
260 .forEach(i -> commonTreatmentBuilder.add(i));
261 TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
262
263 TrafficTreatment treatment = DefaultTrafficTreatment
264 .builder(commonTreatment)
265 .setOutput(egressPoint.port()).build();
266
267 OutboundPacket outboundPacket = new DefaultOutboundPacket(
268 egressPoint.deviceId(), treatment, packet.data());
269 return outboundPacket;
270 }
271
272 /**
273 * Translate the requested a virtual Packet Context into
274 * a physical Packet Context.
275 * This method is designed to support Context's send() method that invoked
276 * by applications.
277 * See {@link org.onosproject.net.packet.PacketContext}
278 *
279 * @param context A handled packet context
yoonseon5a51ef72016-10-26 14:50:09 -0700280 */
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900281 public void devirtualizeContext(VirtualPacketContext context) {
yoonseon5a51ef72016-10-26 14:50:09 -0700282 NetworkId networkId = context.getNetworkId();
283
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900284 TrafficTreatment vTreatment = context.treatmentBuilder().build();
yoonseon5a51ef72016-10-26 14:50:09 -0700285
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900286 DeviceId sendThrough = context.outPacket().sendThrough();
yoonseon5a51ef72016-10-26 14:50:09 -0700287
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900288 PortNumber vOutPortNum = vTreatment.allInstructions().stream()
289 .filter(i -> i.type() == Instruction.Type.OUTPUT)
290 .map(i -> ((Instructions.OutputInstruction) i).port())
291 .findFirst().get();
292
293 TrafficTreatment.Builder commonTreatmentBuilder
294 = DefaultTrafficTreatment.builder();
295 vTreatment.allInstructions().stream()
296 .filter(i -> i.type() != Instruction.Type.OUTPUT)
297 .forEach(i -> commonTreatmentBuilder.add(i));
298 TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
299
300 if (!vOutPortNum.isLogical()) {
301 TrafficTreatment treatment = DefaultTrafficTreatment
302 .builder()
303 .addTreatment(commonTreatment)
304 .setOutput(vOutPortNum)
305 .build();
306
307 emit(networkId, new DefaultOutboundPacket(sendThrough,
308 treatment,
309 context.outPacket().data()));
310 } else {
311 if (vOutPortNum == PortNumber.FLOOD) {
312 Set<VirtualPort> vPorts = vnaService
313 .getVirtualPorts(networkId, sendThrough);
314
315 Set<VirtualPort> outPorts = vPorts.stream()
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700316 .filter(vp -> !vp.number().isLogical())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900317 .filter(vp -> vp.number() !=
318 context.inPacket().receivedFrom().port())
319 .collect(Collectors.toSet());
320
321 for (VirtualPort outPort : outPorts) {
322 TrafficTreatment treatment = DefaultTrafficTreatment
323 .builder()
324 .addTreatment(commonTreatment)
325 .setOutput(outPort.number())
326 .build();
327
328 emit(networkId, new DefaultOutboundPacket(sendThrough,
329 treatment,
330 context.outPacket().data()));
331 }
332 }
yoonseon5a51ef72016-10-26 14:50:09 -0700333 }
yoonseon5a51ef72016-10-26 14:50:09 -0700334 }
335
336 private final class InternalPacketProcessor implements PacketProcessor {
337
338 @Override
339 public void process(PacketContext context) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900340 VirtualPacketContext vContexts = virtualize(context);
yoonseon736a73b2017-01-26 14:27:48 -0800341
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900342 if (vContexts == null) {
343 return;
344 }
345
346 VirtualPacketProviderService service =
347 (VirtualPacketProviderService) providerRegistryService
348 .getProviderService(vContexts.getNetworkId(),
349 VirtualPacketProvider.class);
350 if (service != null) {
351 service.processPacket(vContexts);
352 }
yoonseon5a51ef72016-10-26 14:50:09 -0700353 }
354 }
355}