package net.onrc.onos.core.hostmanager; import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createNiceMock; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.isA; import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.verify; import java.util.Date; import net.floodlightcontroller.core.IFloodlightProviderService; import net.floodlightcontroller.core.IListener.Command; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.IUpdate; import net.floodlightcontroller.core.module.FloodlightModuleContext; import net.floodlightcontroller.core.module.FloodlightModuleException; import net.floodlightcontroller.test.FloodlightTestCase; import net.floodlightcontroller.util.MACAddress; import net.onrc.onos.core.datagrid.IDatagridService; import net.onrc.onos.core.datagrid.IEventChannel; import net.onrc.onos.core.datagrid.IEventChannelListener; import net.onrc.onos.core.packet.ARP; import net.onrc.onos.core.packet.DHCP; import net.onrc.onos.core.packet.Data; import net.onrc.onos.core.packet.Ethernet; import net.onrc.onos.core.packet.IPacket; import net.onrc.onos.core.packet.IPv4; import net.onrc.onos.core.packet.UDP; import net.onrc.onos.core.registry.IControllerRegistryService; import net.onrc.onos.core.topology.ITopologyListener; import net.onrc.onos.core.topology.ITopologyService; import net.onrc.onos.core.topology.MockTopology; import net.onrc.onos.core.util.Dpid; import net.onrc.onos.core.util.PortNumber; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFPacketIn; import org.projectfloodlight.openflow.protocol.OFPacketInReason; import org.projectfloodlight.openflow.protocol.OFType; import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.OFPort; /** * Unit tests for the Host Manager module (HostManger). * These test cases check the result of add/delete host and * verify the result of processPacketIn by injecting fake packets. * floodlightProvider, datagridService, topologyService, * controllerRegistryService, eventChannel are mocked out. */ public class HostManagerTest extends FloodlightTestCase { private IPacket pkt0, pkt1, pkt2, pkt3, pkt4; private IOFSwitch sw1; private long sw1Dpid; private long sw1DevPort, sw1DevPort2; private HostManager hostManager; private OFPacketIn pktIn, pktIn2; private FloodlightModuleContext modContext; private ITopologyService networkGraphService; private IEventChannel<Long, Host> eventChannel; private IFloodlightProviderService floodlightProvider; private Date lastSeenTimestamp; private OFFactory ofact; @Override @Before @SuppressWarnings("unchecked") public void setUp() throws Exception { super.setUp(); MockTopology topology = new MockTopology(); IDatagridService datagridService; IControllerRegistryService controllerRegistryService; topology.createSampleTopology2(); modContext = new FloodlightModuleContext(); floodlightProvider = createMock(IFloodlightProviderService.class); datagridService = createMock(IDatagridService.class); networkGraphService = createMock(ITopologyService.class); controllerRegistryService = createMock(IControllerRegistryService.class); eventChannel = createMock(IEventChannel.class); expect(networkGraphService.getTopology()).andReturn(topology).anyTimes(); networkGraphService.addListener(anyObject(ITopologyListener.class), eq(false)); expectLastCall(); expect(datagridService.createChannel("onos.host", Long.class, Host.class)) .andReturn(eventChannel).once(); expect(topology.getOutgoingLink(new Dpid(1L), PortNumber.uint16((short) 100))).andReturn(null).anyTimes(); expect(datagridService.addListener( eq("onos.host"), anyObject(IEventChannelListener.class), eq(Long.class), eq(Host.class))) .andReturn(eventChannel).once(); replay(datagridService); replay(networkGraphService); replay(controllerRegistryService); modContext.addService(IDatagridService.class, datagridService); modContext.addService(ITopologyService.class, networkGraphService); modContext.addService(IFloodlightProviderService.class, floodlightProvider); modContext.getServiceImpl(IFloodlightProviderService.class); sw1Dpid = 1L; ofact = OFFactories.getFactory(OFVersion.OF_10); sw1 = createMockSwitch(sw1Dpid); replay(sw1); sw1DevPort = 100; sw1DevPort2 = 12L; hostManager = new HostManager(); /* * Broadcast source address */ this.pkt0 = new Ethernet() .setDestinationMACAddress("00:44:33:22:11:33") .setSourceMACAddress("FF:FF:FF:FF:FF:FF") .setEtherType(Ethernet.TYPE_IPV4) .setPayload( new IPv4() .setTtl((byte) 128) .setSourceAddress("192.168.10.1") .setDestinationAddress("192.168.255.255") .setPayload(getUDP(new Data(new byte[]{0x01})))); /* * Normal IPv4 packet */ this.pkt1 = new Ethernet() .setDestinationMACAddress("00:11:22:33:44:55") .setSourceMACAddress("00:44:33:22:11:00") .setEtherType(Ethernet.TYPE_IPV4) .setPayload( new IPv4() .setTtl((byte) 128) .setSourceAddress("192.168.1.1") .setDestinationAddress("192.168.1.2") .setPayload(getUDP(new Data(new byte[]{0x01})))); /* * Same MAC header as pkt1,but not IP address set */ this.pkt2 = new Ethernet() .setSourceMACAddress("00:44:33:22:11:01") .setDestinationMACAddress("00:11:22:33:44:55") .setEtherType(Ethernet.TYPE_IPV4) .setPayload( new IPv4() .setTtl((byte) 128) .setPayload(getUDP(new Data(new byte[]{0x01})))); /* * DHCP packet */ this.pkt3 = new Ethernet() .setSourceMACAddress("00:44:33:22:11:01") .setDestinationMACAddress("00:11:22:33:44:55") .setEtherType(Ethernet.TYPE_IPV4) .setPayload( new IPv4() .setTtl((byte) 128) .setSourceAddress("192.168.1.1") .setDestinationAddress("192.168.1.2") .setPayload(getUDP( new DHCP() .setOpCode(DHCP.OPCODE_REPLY) .setHardwareType(DHCP.HWTYPE_ETHERNET) .setHardwareAddressLength((byte) 6) .setHops((byte) 0) .setTransactionId(0x00003d1d) .setSeconds((short) 0) .setFlags((short) 0) .setClientIPAddress(0) .setYourIPAddress(0) .setServerIPAddress(0) .setGatewayIPAddress(0)))); /* * ARP packet */ this.pkt4 = new Ethernet() .setSourceMACAddress("00:44:33:22:11:01") .setDestinationMACAddress("00:11:22:33:44:55") .setEtherType(Ethernet.TYPE_ARP) .setPayload( new ARP() .setHardwareType(ARP.HW_TYPE_ETHERNET) .setProtocolType(ARP.PROTO_TYPE_IP) .setHardwareAddressLength((byte) 6) .setProtocolAddressLength((byte) 4) .setOpCode(ARP.OP_REPLY) .setSenderHardwareAddress(Ethernet.toMACAddress("00:44:33:22:11:01")) .setSenderProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.1")) .setTargetHardwareAddress(Ethernet.toMACAddress("00:11:22:33:44:55")) .setTargetProtocolAddress(IPv4.toIPv4AddressBytes("192.168.1.2"))); this.pktIn = getPacketIn((short) sw1DevPort); this.pktIn2 = getPacketIn((short) sw1DevPort2); lastSeenTimestamp = new Date(1); } @Override @After public void tearDown() throws Exception { } public IOFSwitch createMockSwitch(Long id) { IOFSwitch mockSwitch = createNiceMock(IOFSwitch.class); expect(mockSwitch.getId()).andReturn(id).anyTimes(); return mockSwitch; } /** * Test set operation on lastSeenTimstamp field in Host. */ @Test public void testSetLastSeenTimestamp() { Ethernet eth = (Ethernet) pkt1; Host srcHost = hostManager.getSourceHostFromPacket(eth, sw1Dpid, sw1DevPort); floodlightProvider.addOFMessageListener(eq(OFType.PACKET_IN), isA(HostManager.class)); srcHost.setLastSeenTimestamp(lastSeenTimestamp); assertEquals(lastSeenTimestamp, srcHost.getLastSeenTimestamp()); } /** * test the functionality to get the source host from Packet header * information. */ @Test public void testGetSourceHostFromPacket() { byte[] address = new byte[] {0x00, 0x44, 0x33, 0x22, 0x11, 0x01}; MACAddress srcMac = new MACAddress(address); int ipAddr = IPv4.toIPv4Address("192.168.1.1"); Host host1 = new Host(srcMac, ipAddr, null, sw1Dpid, sw1DevPort, null); /* * test DHCP packet case */ Ethernet eth = (Ethernet) pkt3; Host host2 = hostManager.getSourceHostFromPacket(eth, sw1Dpid, sw1DevPort); assertEquals(host1, host2); /* * test ARP packet case */ eth = (Ethernet) pkt4; host2 = hostManager.getSourceHostFromPacket(eth, sw1Dpid, sw1DevPort); assertEquals(host1, host2); } /** * This test will invoke addHost to add a new host through Packet pkt1. * @throws FloodlightModuleException */ @Test public void testProcessPacketInAddNewHost() throws FloodlightModuleException { floodlightProvider.addOFMessageListener(eq(OFType.PACKET_IN), isA(HostManager.class)); expectLastCall(); floodlightProvider.publishUpdate(isA(IUpdate.class)); expectLastCall(); replay(floodlightProvider); hostManager.init(modContext); hostManager.startUp(modContext); Command cmd = hostManager.processPacketIn( sw1, pktIn, (Ethernet) pkt1, (short) sw1DevPort); assertEquals(Command.CONTINUE, cmd); verify(floodlightProvider); } /** * Test ProcessPacket function. * @throws FloodlightModuleException */ @Test public void testProcessPacketInHasLink() throws FloodlightModuleException { floodlightProvider.addOFMessageListener(eq(OFType.PACKET_IN), isA(HostManager.class)); expectLastCall(); replay(floodlightProvider); hostManager.init(modContext); hostManager.startUp(modContext); Command cmd = hostManager.processPacketIn( sw1, pktIn2, (Ethernet) pkt1, (short) sw1DevPort2); assertEquals(Command.CONTINUE, cmd); verify(floodlightProvider); } /** * Test return Command.STOP path in processPacketIn method by injecting a broadcast packet. */ @Test public void testProcessPacketInStop() { Command cmd = hostManager.processPacketIn( sw1, pktIn, (Ethernet) pkt0, (short) sw1DevPort); assertEquals(Command.STOP, cmd); } /** * Test add a host from the information from packet. * @throws FloodlightModuleException */ @Test public void testAddHost() throws FloodlightModuleException { Ethernet eth = (Ethernet) pkt1; Long longmac = eth.getSourceMAC().toLong(); Host srcHost = hostManager.getSourceHostFromPacket(eth, sw1Dpid, sw1DevPort); floodlightProvider.addOFMessageListener(eq(OFType.PACKET_IN), isA(HostManager.class)); expectLastCall(); floodlightProvider.publishUpdate(isA(IUpdate.class)); expectLastCall(); replay(floodlightProvider); hostManager.init(modContext); hostManager.startUp(modContext); hostManager.addHost(longmac, srcHost); verify(floodlightProvider); } /** * Test delete a host. * @throws FloodlightModuleException */ @Test public void testDeleteHost() throws FloodlightModuleException { Ethernet eth = (Ethernet) pkt1; Host srcHost = hostManager.getSourceHostFromPacket(eth, sw1Dpid, sw1DevPort); floodlightProvider.addOFMessageListener(eq(OFType.PACKET_IN), isA(HostManager.class)); expectLastCall(); floodlightProvider.publishUpdate(isA(IUpdate.class)); expectLastCall(); replay(floodlightProvider); hostManager.init(modContext); hostManager.startUp(modContext); hostManager.deleteHost(srcHost); verify(floodlightProvider); } /** * Test delete a host by using its source mac address. * @throws FloodlightModuleException */ @Test public void testDeleteHostByMac() throws FloodlightModuleException { Ethernet eth = (Ethernet) pkt1; MACAddress mac = eth.getSourceMAC(); floodlightProvider.addOFMessageListener(eq(OFType.PACKET_IN), isA(HostManager.class)); expectLastCall(); floodlightProvider.publishUpdate(isA(IUpdate.class)); expectLastCall(); replay(floodlightProvider); hostManager.init(modContext); hostManager.startUp(modContext); hostManager.deleteHostByMac(mac); verify(floodlightProvider); } /** * Helper for building PacketIns. Defaults to a flowtable miss. * @param inport the inport field value * @return a PacketIn */ private OFPacketIn getPacketIn(int inport) { return ofact.buildPacketIn() .setInPort(OFPort.of(inport)) .setReason(OFPacketInReason.NO_MATCH) .build(); } private UDP getUDP(IPacket payload) { return (UDP) new UDP() .setSourcePort((short) 5000) .setDestinationPort((short) 5001) .setPayload(payload); } }