blob: c6945f94cf70a9ee7d79ba5ae2ce1beb634d6879 [file] [log] [blame]
Carmelo Casconec0fbbee2016-04-27 18:03:36 -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.provider.bmv2.packet.impl;
18
19import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.onlab.packet.Ethernet;
25import org.onlab.util.ImmutableByteSequence;
Carmelo Casconec0fbbee2016-04-27 18:03:36 -070026import org.onosproject.bmv2.api.runtime.Bmv2Device;
Carmelo Cascone0831efb2016-05-31 14:50:19 -070027import org.onosproject.bmv2.api.service.Bmv2Controller;
28import org.onosproject.bmv2.api.service.Bmv2PacketListener;
Carmelo Casconec0fbbee2016-04-27 18:03:36 -070029import org.onosproject.core.CoreService;
30import org.onosproject.net.ConnectPoint;
31import org.onosproject.net.Device;
32import org.onosproject.net.DeviceId;
Carmelo Cascone0831efb2016-05-31 14:50:19 -070033import org.onosproject.net.Port;
Carmelo Casconec0fbbee2016-04-27 18:03:36 -070034import org.onosproject.net.PortNumber;
35import org.onosproject.net.device.DeviceService;
36import org.onosproject.net.flow.DefaultTrafficTreatment;
37import org.onosproject.net.flow.TrafficTreatment;
38import org.onosproject.net.packet.DefaultInboundPacket;
39import org.onosproject.net.packet.DefaultOutboundPacket;
40import org.onosproject.net.packet.DefaultPacketContext;
41import org.onosproject.net.packet.InboundPacket;
42import org.onosproject.net.packet.OutboundPacket;
43import org.onosproject.net.packet.PacketContext;
44import org.onosproject.net.packet.PacketProgrammable;
45import org.onosproject.net.packet.PacketProvider;
46import org.onosproject.net.packet.PacketProviderRegistry;
47import org.onosproject.net.packet.PacketProviderService;
48import org.onosproject.net.provider.AbstractProvider;
49import org.onosproject.net.provider.ProviderId;
50import org.slf4j.Logger;
51import org.slf4j.LoggerFactory;
52
53import java.nio.ByteBuffer;
Carmelo Cascone0831efb2016-05-31 14:50:19 -070054import java.util.Optional;
55
56import static org.onosproject.net.PortNumber.FLOOD;
57import static org.onosproject.net.flow.DefaultTrafficTreatment.emptyTreatment;
58import static org.onosproject.net.flow.instructions.Instruction.Type.OUTPUT;
59import static org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
Carmelo Casconec0fbbee2016-04-27 18:03:36 -070060
61/**
62 * Implementation of a packet provider for BMv2.
63 */
64@Component(immediate = true)
65public class Bmv2PacketProvider extends AbstractProvider implements PacketProvider {
66
Carmelo Cascone0831efb2016-05-31 14:50:19 -070067 private final Logger log = LoggerFactory.getLogger(Bmv2PacketProvider.class);
Carmelo Casconec0fbbee2016-04-27 18:03:36 -070068 private static final String APP_NAME = "org.onosproject.bmv2";
69
70 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Carmelo Cascone0831efb2016-05-31 14:50:19 -070071 protected Bmv2Controller controller;
Carmelo Casconec0fbbee2016-04-27 18:03:36 -070072
73 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
74 protected CoreService coreService;
75
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected PacketProviderRegistry providerRegistry;
78
79 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
80 protected DeviceService deviceService;
81
82 private PacketProviderService providerService;
83
84 private InternalPacketListener packetListener = new InternalPacketListener();
85
86 /**
87 * Creates a new BMv2 packet provider.
88 */
89 public Bmv2PacketProvider() {
90 super(new ProviderId("bmv2", "org.onosproject.provider.packet"));
91 }
92
93 @Activate
94 protected void activate() {
95 providerService = providerRegistry.register(this);
96 coreService.registerApplication(APP_NAME);
Carmelo Cascone0831efb2016-05-31 14:50:19 -070097 controller.addPacketListener(packetListener);
98 log.info("Started");
Carmelo Casconec0fbbee2016-04-27 18:03:36 -070099 }
100
101 @Deactivate
102 public void deactivate() {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700103 controller.removePacketListener(packetListener);
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700104 providerRegistry.unregister(this);
105 providerService = null;
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700106 log.info("Stopped");
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700107 }
108
109 @Override
110 public void emit(OutboundPacket packet) {
111 if (packet != null) {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700112 DeviceId deviceId = packet.sendThrough();
113 Device device = deviceService.getDevice(deviceId);
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700114 if (device.is(PacketProgrammable.class)) {
115 PacketProgrammable packetProgrammable = device.as(PacketProgrammable.class);
116 packetProgrammable.emit(packet);
117 } else {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700118 log.info("No PacketProgrammable behavior for device {}", deviceId);
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700119 }
120 }
121 }
122
123 /**
124 * Internal packet context implementation.
125 */
126 private class Bmv2PacketContext extends DefaultPacketContext {
127
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700128 Bmv2PacketContext(long time, InboundPacket inPkt, OutboundPacket outPkt, boolean block) {
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700129 super(time, inPkt, outPkt, block);
130 }
131
132 @Override
133 public void send() {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700134
135 if (this.block()) {
136 log.info("Unable to send, packet context not blocked");
137 return;
138 }
139
140 DeviceId deviceId = outPacket().sendThrough();
141 ByteBuffer rawData = outPacket().data();
142
143 TrafficTreatment treatment;
144 if (outPacket().treatment() == null) {
145 treatment = (treatmentBuilder() == null) ? emptyTreatment() : treatmentBuilder().build();
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700146 } else {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700147 treatment = outPacket().treatment();
148 }
149
150 // BMv2 doesn't support FLOOD for packet-outs.
151 // Workaround here is to perform multiple emits, one for each device port != packet inPort.
152 Optional<OutputInstruction> floodInst = treatment.allInstructions()
153 .stream()
154 .filter(i -> i.type().equals(OUTPUT))
155 .map(i -> (OutputInstruction) i)
156 .filter(i -> i.port().equals(FLOOD))
157 .findAny();
158
159 if (floodInst.isPresent() && treatment.allInstructions().size() == 1) {
160 // Only one instruction and is FLOOD. Do the trick.
161 PortNumber inPort = inPacket().receivedFrom().port();
162 deviceService.getPorts(outPacket().sendThrough())
163 .stream()
164 .map(Port::number)
165 .filter(port -> !port.equals(inPort))
166 .map(outPort -> DefaultTrafficTreatment.builder().setOutput(outPort).build())
167 .map(outTreatment -> new DefaultOutboundPacket(deviceId, outTreatment, rawData))
168 .forEach(Bmv2PacketProvider.this::emit);
169 } else {
170 // Not FLOOD treatment, what to do is up to driver.
171 emit(new DefaultOutboundPacket(deviceId, treatment, rawData));
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700172 }
173 }
174 }
175
176 /**
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700177 * Internal packet listener to handle packet-in events received from the BMv2 controller.
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700178 */
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700179 private class InternalPacketListener implements Bmv2PacketListener {
180
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700181 @Override
Carmelo Cascone25f18882016-06-14 19:16:50 -0700182 public void handlePacketIn(Bmv2Device device, int inputPort, ImmutableByteSequence packet) {
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700183 Ethernet ethPkt = new Ethernet();
184 ethPkt.deserialize(packet.asArray(), 0, packet.size());
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700185
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700186 DeviceId deviceId = device.asDeviceId();
187 ConnectPoint receivedFrom = new ConnectPoint(deviceId, PortNumber.portNumber(inputPort));
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700188
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700189 ByteBuffer rawData = ByteBuffer.wrap(packet.asArray());
190
191 InboundPacket inPkt = new DefaultInboundPacket(receivedFrom, ethPkt, rawData);
192 OutboundPacket outPkt = new DefaultOutboundPacket(deviceId, null, rawData);
193
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700194 PacketContext pktCtx = new Bmv2PacketContext(System.currentTimeMillis(), inPkt, outPkt, false);
Carmelo Cascone0831efb2016-05-31 14:50:19 -0700195
Carmelo Casconec0fbbee2016-04-27 18:03:36 -0700196 providerService.processPacket(pktCtx);
197 }
198 }
199}