blob: 5eaa3b045a98f899334db715f8236e8435320f0a [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.
yoonseon5a51ef72016-10-26 14:50:09 -070099 */
100 public DefaultVirtualPacketProvider() {
Thomas Vachuska11b99fc2017-04-27 12:51:04 -0700101 super(new ProviderId("virtual-packet", "org.onosproject.virtual.virtual-packet"));
yoonseon5a51ef72016-10-26 14:50:09 -0700102 }
103
104 @Activate
105 public void activate() {
Thomas Vachuska11b99fc2017-04-27 12:51:04 -0700106 appId = coreService.registerApplication("org.onosproject.virtual.virtual-packet");
yoonseon736a73b2017-01-26 14:27:48 -0800107 providerRegistryService.registerProvider(this);
yoonseon5a51ef72016-10-26 14:50:09 -0700108
yoonseon5a51ef72016-10-26 14:50:09 -0700109 contextMap = Maps.newConcurrentMap();
110
111 log.info("Started");
112 }
113
114 @Deactivate
115 public void deactivate() {
Jonathan Hart66028672017-02-02 16:25:49 -0800116 if (processor != null) {
117 packetService.removeProcessor(processor);
118 }
Thomas Vachuska11b99fc2017-04-27 12:51:04 -0700119 providerRegistryService.unregisterProvider(this);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900120 log.info("Stopped");
yoonseon5a51ef72016-10-26 14:50:09 -0700121 }
122
123 @Modified
124 protected void modified(ComponentContext context) {
125 Dictionary<?, ?> properties = context.getProperties();
126 }
127
128
129 @Override
130 public void emit(NetworkId networkId, OutboundPacket packet) {
131 packetService.emit(devirtualize(networkId, packet));
132 }
133
yoonseonfb4a1db2017-01-31 11:38:30 -0800134 @Override
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900135 public void startPacketHandling(NetworkId networkId) {
136 requestsSet.add(networkId);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900137 if (processor == null) {
138 processor = new InternalPacketProcessor();
139 packetService.addProcessor(processor, PACKET_PROCESSOR_PRIORITY);
140 }
141 }
142
143 @Override
144 public void stopPacketHandling(NetworkId networkId) {
145 requestsSet.remove(networkId);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900146 if (requestsSet.isEmpty()) {
147 packetService.removeProcessor(processor);
148 processor = null;
149 }
yoonseonfb4a1db2017-01-31 11:38:30 -0800150 }
151
yoonseon5a51ef72016-10-26 14:50:09 -0700152 /**
153 * Translate the requested physical PacketContext into a virtual PacketContext.
154 * See {@link org.onosproject.net.packet.OutboundPacket}
155 *
156 * @param context A physical PacketContext be translated
157 * @return A translated virtual PacketContext
158 */
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900159 private VirtualPacketContext virtualize(PacketContext context) {
yoonseon5a51ef72016-10-26 14:50:09 -0700160
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900161 VirtualPort vPort = getMappedVirtualPort(context.inPacket().receivedFrom());
yoonseon5a51ef72016-10-26 14:50:09 -0700162
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900163 if (vPort != null) {
yoonseon5a51ef72016-10-26 14:50:09 -0700164 ConnectPoint cp = new ConnectPoint(vPort.element().id(),
165 vPort.number());
166
167 Ethernet eth = context.inPacket().parsed();
168 eth.setVlanID(Ethernet.VLAN_UNTAGGED);
169
170 InboundPacket inPacket =
171 new DefaultInboundPacket(cp, eth,
172 ByteBuffer.wrap(eth.serialize()));
173
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900174 DefaultOutboundPacket outPkt =
175 new DefaultOutboundPacket(cp.deviceId(),
176 DefaultTrafficTreatment.builder().build(),
177 ByteBuffer.wrap(eth.serialize()));
178
yoonseon5a51ef72016-10-26 14:50:09 -0700179 VirtualPacketContext vContext =
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900180 new VirtualPacketContext(context.time(), inPacket, outPkt,
yoonseon5a51ef72016-10-26 14:50:09 -0700181 false, vPort.networkId(),
182 this);
183
184 contextMap.put(vContext, context);
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900185
186 return vContext;
187 } else {
188 return null;
yoonseon5a51ef72016-10-26 14:50:09 -0700189 }
190
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900191 }
192
193 /**
194 * Find the corresponding virtual port with the physical port.
195 *
196 * @param cp the connect point for the physical network
197 * @return a virtual port
198 */
199 private VirtualPort getMappedVirtualPort(ConnectPoint cp) {
200 Set<TenantId> tIds = vnaService.getTenantIds();
201
202 Set<VirtualNetwork> vNetworks = new HashSet<>();
203 tIds.forEach(tid -> vNetworks.addAll(vnaService.getVirtualNetworks(tid)));
204
205 for (VirtualNetwork vNet : vNetworks) {
206 Set<VirtualDevice> vDevices = vnaService.getVirtualDevices(vNet.id());
207
208 Set<VirtualPort> vPorts = new HashSet<>();
209 vDevices.forEach(dev -> vPorts
210 .addAll(vnaService.getVirtualPorts(dev.networkId(), dev.id())));
211
212 VirtualPort vPort = vPorts.stream()
213 .filter(vp -> vp.realizedBy().equals(cp))
214 .findFirst().orElse(null);
215
216 if (vPort != null) {
217 return vPort;
218 }
219 }
220
221 return null;
yoonseon5a51ef72016-10-26 14:50:09 -0700222 }
223
224 /**
225 * Translate the requested a virtual outbound packet into
226 * a physical OutboundPacket.
227 * See {@link org.onosproject.net.packet.PacketContext}
228 *
229 * @param packet A OutboundPacket to be translated
230 * @return de-virtualized (physical) OutboundPacket
231 */
232 private OutboundPacket devirtualize(NetworkId networkId, OutboundPacket packet) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900233 Set<VirtualPort> vPorts = vnaService
yoonseon5a51ef72016-10-26 14:50:09 -0700234 .getVirtualPorts(networkId, packet.sendThrough());
235
236 PortNumber vOutPortNum = packet.treatment().allInstructions().stream()
237 .filter(i -> i.type() == Instruction.Type.OUTPUT)
238 .map(i -> ((Instructions.OutputInstruction) i).port())
239 .findFirst().get();
240
241 Optional<ConnectPoint> optionalCpOut = vPorts.stream()
242 .filter(v -> v.number().equals(vOutPortNum))
243 .map(v -> v.realizedBy())
244 .findFirst();
245 if (!optionalCpOut.isPresent()) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900246 log.warn("Port {} is not realized yet, in Network {}, Device {}",
yoonseon5a51ef72016-10-26 14:50:09 -0700247 vOutPortNum, networkId, packet.sendThrough());
248 return null;
249 }
250 ConnectPoint egressPoint = optionalCpOut.get();
251
252 TrafficTreatment.Builder commonTreatmentBuilder
253 = DefaultTrafficTreatment.builder();
254 packet.treatment().allInstructions().stream()
255 .filter(i -> i.type() != Instruction.Type.OUTPUT)
256 .forEach(i -> commonTreatmentBuilder.add(i));
257 TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
258
259 TrafficTreatment treatment = DefaultTrafficTreatment
260 .builder(commonTreatment)
261 .setOutput(egressPoint.port()).build();
262
263 OutboundPacket outboundPacket = new DefaultOutboundPacket(
264 egressPoint.deviceId(), treatment, packet.data());
265 return outboundPacket;
266 }
267
268 /**
269 * Translate the requested a virtual Packet Context into
270 * a physical Packet Context.
271 * This method is designed to support Context's send() method that invoked
272 * by applications.
273 * See {@link org.onosproject.net.packet.PacketContext}
274 *
275 * @param context A handled packet context
yoonseon5a51ef72016-10-26 14:50:09 -0700276 */
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900277 public void devirtualizeContext(VirtualPacketContext context) {
yoonseon5a51ef72016-10-26 14:50:09 -0700278 NetworkId networkId = context.getNetworkId();
279
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900280 TrafficTreatment vTreatment = context.treatmentBuilder().build();
yoonseon5a51ef72016-10-26 14:50:09 -0700281
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900282 DeviceId sendThrough = context.outPacket().sendThrough();
yoonseon5a51ef72016-10-26 14:50:09 -0700283
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900284 PortNumber vOutPortNum = vTreatment.allInstructions().stream()
285 .filter(i -> i.type() == Instruction.Type.OUTPUT)
286 .map(i -> ((Instructions.OutputInstruction) i).port())
287 .findFirst().get();
288
289 TrafficTreatment.Builder commonTreatmentBuilder
290 = DefaultTrafficTreatment.builder();
291 vTreatment.allInstructions().stream()
292 .filter(i -> i.type() != Instruction.Type.OUTPUT)
293 .forEach(i -> commonTreatmentBuilder.add(i));
294 TrafficTreatment commonTreatment = commonTreatmentBuilder.build();
295
296 if (!vOutPortNum.isLogical()) {
297 TrafficTreatment treatment = DefaultTrafficTreatment
298 .builder()
299 .addTreatment(commonTreatment)
300 .setOutput(vOutPortNum)
301 .build();
302
303 emit(networkId, new DefaultOutboundPacket(sendThrough,
304 treatment,
305 context.outPacket().data()));
306 } else {
307 if (vOutPortNum == PortNumber.FLOOD) {
308 Set<VirtualPort> vPorts = vnaService
309 .getVirtualPorts(networkId, sendThrough);
310
311 Set<VirtualPort> outPorts = vPorts.stream()
Yoonseon Han5cf483a2017-04-19 23:14:00 -0700312 .filter(vp -> !vp.number().isLogical())
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900313 .filter(vp -> vp.number() !=
314 context.inPacket().receivedFrom().port())
315 .collect(Collectors.toSet());
316
317 for (VirtualPort outPort : outPorts) {
318 TrafficTreatment treatment = DefaultTrafficTreatment
319 .builder()
320 .addTreatment(commonTreatment)
321 .setOutput(outPort.number())
322 .build();
323
324 emit(networkId, new DefaultOutboundPacket(sendThrough,
325 treatment,
326 context.outPacket().data()));
327 }
328 }
yoonseon5a51ef72016-10-26 14:50:09 -0700329 }
yoonseon5a51ef72016-10-26 14:50:09 -0700330 }
331
332 private final class InternalPacketProcessor implements PacketProcessor {
333
334 @Override
335 public void process(PacketContext context) {
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900336 VirtualPacketContext vContexts = virtualize(context);
yoonseon736a73b2017-01-26 14:27:48 -0800337
Yoonseon Hanc8089db2017-03-22 20:22:12 +0900338 if (vContexts == null) {
339 return;
340 }
341
342 VirtualPacketProviderService service =
343 (VirtualPacketProviderService) providerRegistryService
344 .getProviderService(vContexts.getNetworkId(),
345 VirtualPacketProvider.class);
346 if (service != null) {
347 service.processPacket(vContexts);
348 }
yoonseon5a51ef72016-10-26 14:50:09 -0700349 }
350 }
351}