blob: d69a0acc17442679d62c3548fc3e0144a9713fb4 [file] [log] [blame]
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.provider.nil;
import org.onlab.packet.Ethernet;
import org.onlab.packet.ICMP;
import org.onlab.util.Timer;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceAdminService;
import org.onosproject.net.host.HostService;
import org.onosproject.net.packet.DefaultInboundPacket;
import org.onosproject.net.packet.DefaultPacketContext;
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.packet.PacketProvider;
import org.onosproject.net.packet.PacketProviderService;
import org.slf4j.Logger;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.google.common.collect.ImmutableList.copyOf;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.onosproject.net.MastershipRole.MASTER;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Provider which generates simulated packets and acts as a sink for outbound
* packets. To be used for benchmarking only.
*/
class NullPacketProvider extends NullProviders.AbstractNullProvider
implements PacketProvider {
private static final int INITIAL_DELAY = 5;
private final Logger log = getLogger(getClass());
// Arbitrary host src/dst
private static final int SRC_HOST = 2;
private static final int DST_HOST = 5;
// Time between event firing, in milliseconds
private int delay;
// TODO: use host service to pick legitimate hosts connected to devices
private HostService hostService;
private PacketProviderService providerService;
private List<Device> devices;
private int currentDevice = 0;
private Timeout timeout;
/**
* Starts the packet generation process.
*
* @param packetRate packets per second
* @param hostService host service
* @param deviceService device service
* @param providerService packet provider service
*/
void start(int packetRate, HostService hostService,
DeviceAdminService deviceService,
PacketProviderService providerService) {
this.hostService = hostService;
this.providerService = providerService;
this.devices = copyOf(deviceService.getDevices()).stream()
.filter(d -> deviceService.getRole(d.id()) == MASTER)
.collect(Collectors.toList());
adjustRate(packetRate);
timeout = Timer.newTimeout(new PacketDriverTask(), INITIAL_DELAY, SECONDS);
}
/**
* Adjusts packet rate.
*
* @param packetRate new packet rate
*/
void adjustRate(int packetRate) {
boolean needsRestart = delay == 0 && packetRate > 0;
delay = packetRate > 0 ? 1000 / packetRate : 0;
if (needsRestart) {
timeout = Timer.newTimeout(new PacketDriverTask(), 1, MILLISECONDS);
}
log.info("Settings: packetRate={}, delay={}", packetRate, delay);
}
/**
* Stops the packet generation process.
*/
void stop() {
if (timeout != null) {
timeout.cancel();
}
}
@Override
public void emit(OutboundPacket packet) {
// We don't have a network to emit to. Keep a counter here, maybe?
}
/**
* Generates packet events at a given rate.
*/
private class PacketDriverTask implements TimerTask {
// Filler echo request
ICMP icmp;
Ethernet eth;
PacketDriverTask() {
icmp = new ICMP();
icmp.setIcmpType((byte) 8).setIcmpCode((byte) 0).setChecksum((short) 0);
eth = new Ethernet();
eth.setEtherType(Ethernet.TYPE_IPV4);
eth.setPayload(icmp);
}
@Override
public void run(Timeout to) {
if (!devices.isEmpty() && !to.isCancelled() && delay > 0) {
sendEvent(devices.get(Math.min(currentDevice, devices.size() - 1)));
currentDevice = (currentDevice + 1) % devices.size();
timeout = to.timer().newTimeout(to.task(), delay, TimeUnit.MILLISECONDS);
}
}
private void sendEvent(Device device) {
// Make it look like things came from ports attached to hosts
eth.setSourceMACAddress("00:00:00:10:00:0" + SRC_HOST)
.setDestinationMACAddress("00:00:00:10:00:0" + DST_HOST);
InboundPacket inPkt = new DefaultInboundPacket(
new ConnectPoint(device.id(), PortNumber.portNumber(SRC_HOST)),
eth, ByteBuffer.wrap(eth.serialize()));
providerService.processPacket(new NullPacketContext(inPkt, null));
}
}
// Minimal PacketContext to make core and applications happy.
private final class NullPacketContext extends DefaultPacketContext {
private NullPacketContext(InboundPacket inPkt, OutboundPacket outPkt) {
super(System.currentTimeMillis(), inPkt, outPkt, false);
}
@Override
public void send() {
// We don't send anything out.
}
}
}