package net.onrc.onos.apps.sdnip; import java.net.InetAddress; import java.util.List; import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import net.floodlightcontroller.util.MACAddress; import net.onrc.onos.apps.proxyarp.IArpRequester; import net.onrc.onos.apps.proxyarp.IProxyArpService; /** * Test version of the IProxyArpService which is used to simulate delays in * receiving ARP replies, as you would see in a real system due to the time * it takes to proxy ARP packets to/from the host. Requests are asynchronous, * and replies may come back to the requestor in a different order than the * requests were sent, which again you would expect to see in a real system. */ public class TestProxyArpService implements IProxyArpService { /** * The maximum possible delay before an ARP reply is received. */ private static final int MAX_ARP_REPLY_DELAY = 30; // milliseconds /** * The probability that we already have the MAC address cached when the * caller calls {@link #getMacAddress(InetAddress)}. */ private static final float MAC_ALREADY_KNOWN_PROBABILITY = 0.3f; private final ScheduledExecutorService replyTaskExecutor; private final Random random; /** * Class constructor. */ public TestProxyArpService() { replyTaskExecutor = Executors.newSingleThreadScheduledExecutor(); random = new Random(); } /** * Task used to reply to ARP requests from a different thread. Replies * usually come on a different thread in the real system, so we need to * ensure we test this behaviour. */ private static class ReplyTask implements Runnable { private IArpRequester requestor; private InetAddress ipAddress; private MACAddress macAddress; /** * Class constructor. * * @param requestor the client who requested the MAC address * @param ipAddress the target IP address of the request * @param macAddress the MAC address in the ARP reply */ public ReplyTask(IArpRequester requestor, InetAddress ipAddress, MACAddress macAddress) { this.requestor = requestor; this.ipAddress = ipAddress; this.macAddress = macAddress; } @Override public void run() { requestor.arpResponse(ipAddress, macAddress); } } @Override public MACAddress getMacAddress(InetAddress ipAddress) { float replyChance = random.nextFloat(); if (replyChance < MAC_ALREADY_KNOWN_PROBABILITY) { // Some percentage of the time we already know the MAC address, so // we reply directly when the requestor asks for the MAC address return SdnIpTest.generateMacAddress(ipAddress); } return null; } @Override public void sendArpRequest(InetAddress ipAddress, IArpRequester requester, boolean retry) { // Randomly select an amount of time to delay the reply coming back to // the requestor (simulating time taken to proxy the request to a // network host). int delay = random.nextInt(MAX_ARP_REPLY_DELAY); MACAddress macAddress = SdnIpTest.generateMacAddress(ipAddress); ReplyTask replyTask = new ReplyTask(requester, ipAddress, macAddress); replyTaskExecutor.schedule(replyTask , delay, TimeUnit.MILLISECONDS); } @Override public List<String> getMappings() { // We don't care about this method for the current test use cases return null; } }