blob: edf95de36aaef1ebdbdf6b2979ccef6dd0b78fe5 [file] [log] [blame]
wu2883c762017-07-20 16:24:48 +08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
wu2883c762017-07-20 16:24:48 +08003 *
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.p4runtime.packet.impl;
18
Carmelo Cascone4becd352019-08-27 16:06:55 -070019import org.onlab.packet.EthType;
pierventre31f28c052021-02-05 17:56:22 +010020import org.onlab.util.PredictableExecutor;
21import org.onosproject.cfg.ComponentConfigService;
Andrea Campanella14e196d2017-07-24 18:11:36 +020022import org.onosproject.mastership.MastershipService;
wu2883c762017-07-20 16:24:48 +080023import org.onosproject.net.Device;
24import org.onosproject.net.DeviceId;
25import org.onosproject.net.device.DeviceService;
26import org.onosproject.net.flow.TrafficTreatment;
27import org.onosproject.net.packet.DefaultOutboundPacket;
28import org.onosproject.net.packet.DefaultPacketContext;
29import org.onosproject.net.packet.InboundPacket;
30import org.onosproject.net.packet.OutboundPacket;
Andrea Campanella288b2732017-07-28 14:16:16 +020031import org.onosproject.net.packet.PacketContext;
wu2883c762017-07-20 16:24:48 +080032import org.onosproject.net.packet.PacketProgrammable;
33import org.onosproject.net.packet.PacketProvider;
34import org.onosproject.net.packet.PacketProviderRegistry;
35import org.onosproject.net.packet.PacketProviderService;
Andrea Campanella288b2732017-07-28 14:16:16 +020036import org.onosproject.net.pi.model.PiPipelineInterpreter;
37import org.onosproject.net.pi.runtime.PiPacketOperation;
wu2883c762017-07-20 16:24:48 +080038import org.onosproject.net.provider.AbstractProvider;
39import org.onosproject.net.provider.ProviderId;
40import org.onosproject.p4runtime.api.P4RuntimeController;
41import org.onosproject.p4runtime.api.P4RuntimeEvent;
42import org.onosproject.p4runtime.api.P4RuntimeEventListener;
Andrea Campanella288b2732017-07-28 14:16:16 +020043import org.onosproject.p4runtime.api.P4RuntimePacketIn;
pierventre31f28c052021-02-05 17:56:22 +010044import org.osgi.service.component.ComponentContext;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080045import org.osgi.service.component.annotations.Activate;
46import org.osgi.service.component.annotations.Component;
47import org.osgi.service.component.annotations.Deactivate;
pierventre31f28c052021-02-05 17:56:22 +010048import org.osgi.service.component.annotations.Modified;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080049import org.osgi.service.component.annotations.Reference;
50import org.osgi.service.component.annotations.ReferenceCardinality;
wu2883c762017-07-20 16:24:48 +080051import org.slf4j.Logger;
52
53import java.nio.ByteBuffer;
pierventre31f28c052021-02-05 17:56:22 +010054import java.util.Dictionary;
55import java.util.concurrent.ExecutorService;
wu2883c762017-07-20 16:24:48 +080056
pierventre31f28c052021-02-05 17:56:22 +010057import static com.google.common.base.Strings.isNullOrEmpty;
58import static org.onlab.util.Tools.get;
59import static org.onlab.util.Tools.groupedThreads;
wu2883c762017-07-20 16:24:48 +080060import static org.onosproject.net.flow.DefaultTrafficTreatment.emptyTreatment;
pierventre31f28c052021-02-05 17:56:22 +010061import static org.onosproject.provider.p4runtime.packet.impl.OsgiPropertyConstants.P4RUNTIME_PACKET_PROVIDER_WORKERS;
62import static org.onosproject.provider.p4runtime.packet.impl.OsgiPropertyConstants.P4RUNTIME_PACKET_PROVIDER_WORKERS_DEFAULT;
wu2883c762017-07-20 16:24:48 +080063import static org.slf4j.LoggerFactory.getLogger;
64
65/**
66 * Implementation of a packet provider for P4Runtime device.
67 */
pierventre31f28c052021-02-05 17:56:22 +010068@Component(immediate = true,
69 property = {
70 P4RUNTIME_PACKET_PROVIDER_WORKERS + ":Integer=" + P4RUNTIME_PACKET_PROVIDER_WORKERS_DEFAULT,
71 })
wu2883c762017-07-20 16:24:48 +080072public class P4RuntimePacketProvider extends AbstractProvider implements PacketProvider {
73
74 private final Logger log = getLogger(getClass());
75
Ray Milkeyd84f89b2018-08-17 14:54:17 -070076 @Reference(cardinality = ReferenceCardinality.MANDATORY)
wu2883c762017-07-20 16:24:48 +080077 protected P4RuntimeController controller;
78
Ray Milkeyd84f89b2018-08-17 14:54:17 -070079 @Reference(cardinality = ReferenceCardinality.MANDATORY)
wu2883c762017-07-20 16:24:48 +080080 protected PacketProviderRegistry providerRegistry;
81
Ray Milkeyd84f89b2018-08-17 14:54:17 -070082 @Reference(cardinality = ReferenceCardinality.MANDATORY)
wu2883c762017-07-20 16:24:48 +080083 protected DeviceService deviceService;
84
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Andrea Campanella14e196d2017-07-24 18:11:36 +020086 protected MastershipService mastershipService;
87
pierventre31f28c052021-02-05 17:56:22 +010088 @Reference(cardinality = ReferenceCardinality.MANDATORY)
89 protected ComponentConfigService cfgService;
90
wu2883c762017-07-20 16:24:48 +080091 private PacketProviderService providerService;
92
93 private InternalPacketListener packetListener = new InternalPacketListener();
94
pierventre31f28c052021-02-05 17:56:22 +010095 /** Number of P4Runtime packet provider workers. */
96 private int workers = P4RUNTIME_PACKET_PROVIDER_WORKERS_DEFAULT;
97 // Predictable executor to stitch the packet processing always to the same thread
98 private PredictableExecutor packetWorkers;
99
wu2883c762017-07-20 16:24:48 +0800100 /**
101 * Creates a new P4Runtime packet provider.
102 */
103 public P4RuntimePacketProvider() {
104 super(new ProviderId("p4runtime", "org.onosproject.provider.p4runtime.packet"));
105 }
106
107 @Activate
pierventre31f28c052021-02-05 17:56:22 +0100108 protected void activate(ComponentContext context) {
109 cfgService.registerProperties(getClass());
wu2883c762017-07-20 16:24:48 +0800110 providerService = providerRegistry.register(this);
pierventre31f28c052021-02-05 17:56:22 +0100111 modified(context);
wu2883c762017-07-20 16:24:48 +0800112 controller.addListener(packetListener);
113 log.info("Started");
114 }
115
116 @Deactivate
117 public void deactivate() {
pierventre31f28c052021-02-05 17:56:22 +0100118 cfgService.unregisterProperties(getClass(), false);
wu2883c762017-07-20 16:24:48 +0800119 controller.removeListener(packetListener);
120 providerRegistry.unregister(this);
121 providerService = null;
pierventre31f28c052021-02-05 17:56:22 +0100122 stopWorkersIfNeeded();
wu2883c762017-07-20 16:24:48 +0800123 log.info("Stopped");
124 }
125
pierventre31f28c052021-02-05 17:56:22 +0100126 @Modified
127 protected void modified(ComponentContext context) {
128 if (context != null) {
129 Dictionary<?, ?> properties = context.getProperties();
130 int newWorkers;
131 try {
132 String s = get(properties, P4RUNTIME_PACKET_PROVIDER_WORKERS);
133 newWorkers = isNullOrEmpty(s) ? workers : Integer.parseInt(s.trim());
134 } catch (NumberFormatException | ClassCastException e) {
135 newWorkers = workers;
136 }
137
138 // Stop previous executor and start a new one when there are changes in the config
139 // OR during the start up of the service
140 if (newWorkers != workers || packetWorkers == null) {
141 workers = newWorkers;
142 stopWorkersIfNeeded();
143 packetWorkers = new PredictableExecutor(workers, groupedThreads("onos/p4rt",
144 "packet-worker-%d", log));
145 log.info("Settings: p4RuntimePacketProviderWorkers={}", workers);
146 }
147 }
148 }
149
wu2883c762017-07-20 16:24:48 +0800150 @Override
151 public void emit(OutboundPacket packet) {
152 if (packet != null) {
153 DeviceId deviceId = packet.sendThrough();
154 Device device = deviceService.getDevice(deviceId);
Andrea Campanella14e196d2017-07-24 18:11:36 +0200155 if (device.is(PacketProgrammable.class) && mastershipService.isLocalMaster(deviceId)) {
wu2883c762017-07-20 16:24:48 +0800156 PacketProgrammable packetProgrammable = device.as(PacketProgrammable.class);
157 packetProgrammable.emit(packet);
158 } else {
159 log.warn("No PacketProgrammable behavior for device {}", deviceId);
160 }
161 }
162 }
163
pierventre31f28c052021-02-05 17:56:22 +0100164 private void stopWorkersIfNeeded() {
165 if (packetWorkers != null) {
166 ExecutorService oldWorkerExecutor = packetWorkers;
167 oldWorkerExecutor.shutdown();
168 packetWorkers = null;
169 }
170 }
171
Carmelo Cascone4becd352019-08-27 16:06:55 -0700172 private EthType.EtherType getEtherType(ByteBuffer data) {
173 final short shortEthType = data.getShort(12);
174 data.rewind();
175 return EthType.EtherType.lookup(shortEthType);
176 }
177
wu2883c762017-07-20 16:24:48 +0800178 /**
179 * Internal packet context implementation.
180 */
181 private class P4RuntimePacketContext extends DefaultPacketContext {
182
183 P4RuntimePacketContext(long time, InboundPacket inPkt, OutboundPacket outPkt, boolean block) {
184 super(time, inPkt, outPkt, block);
185 }
186
187 @Override
188 public void send() {
189
190 if (this.block()) {
191 log.info("Unable to send, packet context is blocked");
192 return;
193 }
194
195 DeviceId deviceId = outPacket().sendThrough();
196 ByteBuffer rawData = outPacket().data();
197
198 TrafficTreatment treatment;
199 if (outPacket().treatment() == null) {
200 treatment = (treatmentBuilder() == null) ? emptyTreatment() : treatmentBuilder().build();
201 } else {
202 treatment = outPacket().treatment();
203 }
204
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200205 OutboundPacket outboundPacket = new DefaultOutboundPacket(deviceId, treatment, rawData);
Carmelo Cascone2cad9ef2017-08-01 21:52:07 +0200206
207 emit(outboundPacket);
wu2883c762017-07-20 16:24:48 +0800208 }
209 }
210
pierventre31f28c052021-02-05 17:56:22 +0100211 private void handleP4RuntimeEvent(P4RuntimeEvent event) {
212 //Mastership message is sent to everybody but picked up only by master.
213 //FIXME we need the device ID into p4RuntimeEvnetSubject to check for mastsership
214 if (!(event.subject() instanceof P4RuntimePacketIn) || event.type() != P4RuntimeEvent.Type.PACKET_IN) {
215 log.debug("Unrecognized event type {}, discarding", event.type());
216 // Not a packet-in event, ignore it.
217 return;
218 }
219 P4RuntimePacketIn eventSubject = (P4RuntimePacketIn) event.subject();
220 DeviceId deviceId = eventSubject.deviceId();
221
222 Device device = deviceService.getDevice(eventSubject.deviceId());
223 if (device == null) {
224 log.warn("Unable to process packet-in from {}, device is null in the core", deviceId);
225 return;
226 }
227
228 if (!device.is(PiPipelineInterpreter.class)) {
229 log.warn("Unable to process packet-in from {}, device has no PiPipelineInterpreter behaviour",
230 deviceId);
231 return;
232 }
233
234 PiPacketOperation operation = eventSubject.packetOperation();
235 InboundPacket inPkt;
236 try {
237 inPkt = device.as(PiPipelineInterpreter.class).mapInboundPacket(operation, deviceId);
238 } catch (PiPipelineInterpreter.PiInterpreterException e) {
239 log.warn("Unable to interpret inbound packet from {}: {}", deviceId, e.getMessage());
240 return;
241 }
242
243 if (log.isTraceEnabled()) {
244 final EthType.EtherType etherType = getEtherType(inPkt.unparsed());
245 log.trace("Received PACKET-IN <<< device={} ingress_port={} eth_type={}",
246 inPkt.receivedFrom().deviceId(), inPkt.receivedFrom().port(),
247 etherType.ethType().toString());
248 }
249
250 if (inPkt == null) {
251 log.debug("Received null inbound packet. Ignoring.");
252 return;
253 }
254
255 OutboundPacket outPkt = new DefaultOutboundPacket(eventSubject.deviceId(), null,
256 operation.data().asReadOnlyBuffer());
257 PacketContext pktCtx = new P4RuntimePacketContext(System.currentTimeMillis(), inPkt, outPkt, false);
258
259 // Pushing the packet context up for processing.
260 providerService.processPacket(pktCtx);
261 }
262
wu2883c762017-07-20 16:24:48 +0800263 /**
264 * Internal packet listener to handle packet-in events received from the P4Runtime controller.
265 */
266 private class InternalPacketListener implements P4RuntimeEventListener {
267
wu2883c762017-07-20 16:24:48 +0800268 @Override
269 public void event(P4RuntimeEvent event) {
pierventre31f28c052021-02-05 17:56:22 +0100270 // Offload to another executor to prevent the ejection of the listener - it uses
271 // the device id to stitch the packets coming from a device always to the same worker
272 packetWorkers.execute(() -> handleP4RuntimeEvent(event), event.subject().deviceId().hashCode());
wu2883c762017-07-20 16:24:48 +0800273 }
274 }
275}