/* * 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.jboss.netty.util.HashedWheelTimer; import org.jboss.netty.util.Timeout; import org.jboss.netty.util.TimerTask; 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 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 HashedWheelTimer timer = Timer.getTimer(); 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 = timer.newTimeout(to.getTask(), 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. } } }