blob: 1206ce3d298f3ad237ca4239406cd89a662e35fc [file] [log] [blame]
/*
* Copyright 2018-present Open Networking Foundation
*
* 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.openstacknetworking.impl;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.DHCP;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IPv4;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.UDP;
import org.onlab.packet.dhcp.DhcpOption;
import org.onosproject.cfg.ComponentConfigAdapter;
import org.onosproject.cluster.ClusterServiceAdapter;
import org.onosproject.cluster.LeadershipServiceAdapter;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreServiceAdapter;
import org.onosproject.core.DefaultApplicationId;
import org.onosproject.net.DeviceId;
import org.onosproject.net.HostId;
import org.onosproject.net.PortNumber;
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.PacketContext;
import org.onosproject.net.packet.PacketProcessor;
import org.onosproject.net.packet.PacketServiceAdapter;
import org.onosproject.openstacknetworking.api.InstancePort;
import org.openstack4j.model.network.AllowedAddressPair;
import org.openstack4j.model.network.HostRoute;
import org.openstack4j.model.network.IP;
import org.openstack4j.model.network.IPVersionType;
import org.openstack4j.model.network.Ipv6AddressMode;
import org.openstack4j.model.network.Ipv6RaMode;
import org.openstack4j.model.network.Pool;
import org.openstack4j.model.network.Port;
import org.openstack4j.model.network.State;
import org.openstack4j.model.network.Subnet;
import org.openstack4j.model.network.builder.PortBuilder;
import org.openstack4j.model.network.builder.SubnetBuilder;
import org.openstack4j.openstack.networking.domain.NeutronIP;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.onosproject.net.NetTestTools.connectPoint;
/**
* Unit tests for Openstack switching DHCP handler.
*/
public class OpenstackSwitchingDhcpHandlerTest {
private OpenstackSwitchingDhcpHandler dhcpHandler;
protected PacketProcessor packetProcessor;
private static final HostId CLIENT_HOST =
HostId.hostId(MacAddress.valueOf("1a:1a:1a:1a:1a:1a"));
private static final String EXPECTED_IP = "10.2.0.2";
private static final String EXPECTED_CIDR = "10.2.0.0/24";
private static final String EXPECTED_GATEWAY = "10.2.0.1";
private static final Ip4Address BROADCAST =
Ip4Address.valueOf("255.255.255.255");
private static final int TRANSACTION_ID = 1986;
private static final String IP_SUBNET_ID = "1";
private DHCP.MsgType testMsgType;
@Before
public void setUp() {
dhcpHandler = new OpenstackSwitchingDhcpHandler();
dhcpHandler.coreService = new TestCoreService();
dhcpHandler.configService = new TestConfigService();
dhcpHandler.packetService = new TestPacketService();
dhcpHandler.instancePortService = new TestInstancePortService();
dhcpHandler.osNetworkService = new TestOpenstackNetworkService();
dhcpHandler.osNodeService = new TestOpenstackNodeService();
dhcpHandler.osFlowRuleService = new TestOpenstackFlowRuleService();
dhcpHandler.clusterService = new TestClusterService();
dhcpHandler.leadershipService = new TestLeadershipService();
dhcpHandler.activate();
}
@After
public void tearDown() {
dhcpHandler.deactivate();
}
/**
* Tests the response to a DHCP Discover Packet.
*/
@Test
public void testDiscover() {
Ethernet dhcpDiscover = constructDhcpPacket(DHCP.MsgType.DHCPDISCOVER);
testMsgType = DHCP.MsgType.DHCPDISCOVER;
sendPacket(dhcpDiscover);
}
/**
* Tests the response to a DHCP Request Packet.
*/
@Test
public void testRequest() {
Ethernet dhcpRequest = constructDhcpPacket(DHCP.MsgType.DHCPREQUEST);
testMsgType = DHCP.MsgType.DHCPREQUEST;
sendPacket(dhcpRequest);
}
/**
* Sends an Ethernet packet to the process method of the Packet Processor.
*
* @param ethernet Ethernet packet
*/
private void sendPacket(Ethernet ethernet) {
final ByteBuffer byteBuffer = ByteBuffer.wrap(ethernet.serialize());
InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
ethernet,
byteBuffer);
PacketContext context = new TestPacketContext(127L, inPacket, null, false);
packetProcessor.process(context);
}
/**
* Validates the contents of the packet sent by the OpenstackSwitchingDhcpHandler.
*
* @param packet Ethernet packet received
*/
private void validatePacket(Ethernet packet) {
DHCP dhcpPacket = (DHCP) packet.getPayload().getPayload().getPayload();
assertEquals(MacAddress.valueOf(dhcpPacket.getClientHardwareAddress()), CLIENT_HOST.mac());
assertEquals(Ip4Address.valueOf(dhcpPacket.getYourIPAddress()), Ip4Address.valueOf(EXPECTED_IP));
assertEquals(dhcpPacket.getTransactionId(), TRANSACTION_ID);
if (testMsgType == DHCP.MsgType.DHCPDISCOVER) {
assertEquals(dhcpPacket.getPacketType(), DHCP.MsgType.DHCPOFFER);
}
if (testMsgType == DHCP.MsgType.DHCPREQUEST) {
assertEquals(dhcpPacket.getPacketType(), DHCP.MsgType.DHCPACK);
}
}
/**
* Constructs an Ethernet packet containing a DHCP payload.
*
* @param msgType DHCP message type
* @return Ethernet packet
*/
private Ethernet constructDhcpPacket(DHCP.MsgType msgType) {
// Ethernet frame
Ethernet ethFrame = new Ethernet();
ethFrame.setSourceMACAddress(CLIENT_HOST.mac());
ethFrame.setDestinationMACAddress(MacAddress.BROADCAST);
ethFrame.setEtherType(Ethernet.TYPE_IPV4);
ethFrame.setVlanID((short) 2);
// IP packet
IPv4 ipv4Pkt = new IPv4();
ipv4Pkt.setSourceAddress(0);
ipv4Pkt.setDestinationAddress(BROADCAST.toInt());
ipv4Pkt.setTtl((byte) 127);
// UDP datagram
UDP udpDatagram = new UDP();
udpDatagram.setSourcePort((byte) UDP.DHCP_CLIENT_PORT);
udpDatagram.setDestinationPort((byte) UDP.DHCP_SERVER_PORT);
// DHCP payload
DHCP dhcp = new DHCP();
dhcp.setOpCode(DHCP.OPCODE_REQUEST);
dhcp.setYourIPAddress(0);
dhcp.setServerIPAddress(0);
dhcp.setTransactionId(TRANSACTION_ID);
dhcp.setClientHardwareAddress(CLIENT_HOST.mac().toBytes());
dhcp.setHardwareType(DHCP.HWTYPE_ETHERNET);
dhcp.setHardwareAddressLength((byte) 6);
// DHCP options start...
DhcpOption option = new DhcpOption();
List<DhcpOption> optionList = new ArrayList<>();
// DHCP message type
option.setCode(DHCP.DHCPOptionCode.OptionCode_MessageType.getValue());
option.setLength((byte) 1);
byte[] optionData = {(byte) msgType.getValue()};
option.setData(optionData);
optionList.add(option);
// DHCP requested IP address
option = new DhcpOption();
option.setCode(DHCP.DHCPOptionCode.OptionCode_RequestedIP.getValue());
option.setLength((byte) 4);
optionData = Ip4Address.valueOf(EXPECTED_IP).toOctets();
option.setData(optionData);
optionList.add(option);
// DHCP options end...
option = new DhcpOption();
option.setCode(DHCP.DHCPOptionCode.OptionCode_END.getValue());
option.setLength((byte) 1);
optionList.add(option);
dhcp.setOptions(optionList);
udpDatagram.setPayload(dhcp);
ipv4Pkt.setPayload(udpDatagram);
ethFrame.setPayload(ipv4Pkt);
return ethFrame;
}
/**
* Mocks the CoreService.
*/
private class TestCoreService extends CoreServiceAdapter {
@Override
public ApplicationId registerApplication(String name) {
return new DefaultApplicationId(100, "dhcpTestApp");
}
}
/**
* Mocks the ComponentConfigRegistry.
*/
private class TestConfigService extends ComponentConfigAdapter {
}
/**
* Mocks the ClusterService.
*/
private class TestClusterService extends ClusterServiceAdapter {
}
/**
* Mocks the LeadershipService.
*/
private class TestLeadershipService extends LeadershipServiceAdapter {
}
/**
* Mocks the InstancePortService.
*/
private class TestInstancePortService extends InstancePortServiceAdapter {
@Override
public InstancePort instancePort(MacAddress macAddress) {
return new TestInstancePort();
}
}
/**
* Mocks the OpenstackNetworkService.
*/
private class TestOpenstackNetworkService extends OpenstackNetworkServiceAdapter {
@Override
public Port port(String portId) {
return new TestPort();
}
@Override
public Subnet subnet(String subnetId) {
return new TestSubnet();
}
/**
* Mocks the Neutron port.
*/
private class TestPort implements Port {
@Override
public State getState() {
return null;
}
@Override
public boolean isAdminStateUp() {
return false;
}
@Override
public String getNetworkId() {
return null;
}
@Override
public String getDeviceId() {
return null;
}
@Override
public String getDeviceOwner() {
return null;
}
@Override
public Set<? extends IP> getFixedIps() {
return ImmutableSet.of(new NeutronIP(EXPECTED_IP, IP_SUBNET_ID));
}
@Override
public Set<? extends AllowedAddressPair> getAllowedAddressPairs() {
return null;
}
@Override
public String getMacAddress() {
return null;
}
@Override
public List<String> getSecurityGroups() {
return null;
}
@Override
public Boolean isPortSecurityEnabled() {
return null;
}
@Override
public String getHostId() {
return null;
}
@Override
public String getVifType() {
return null;
}
@Override
public Map<String, Object> getVifDetails() {
return null;
}
@Override
public String getvNicType() {
return null;
}
@Override
public Map<String, Object> getProfile() {
return null;
}
@Override
public PortBuilder toBuilder() {
return null;
}
@Override
public String getTenantId() {
return null;
}
@Override
public void setTenantId(String tenantId) {
}
@Override
public String getName() {
return null;
}
@Override
public void setName(String name) {
}
@Override
public String getId() {
return null;
}
@Override
public void setId(String id) {
}
}
/**
* Mocks the Neutron subnet.
*/
private class TestSubnet implements Subnet {
@Override
public boolean isDHCPEnabled() {
return false;
}
@Override
public String getNetworkId() {
return null;
}
@Override
public List<String> getDnsNames() {
return Lists.newArrayList();
}
@Override
public List<? extends Pool> getAllocationPools() {
return null;
}
@Override
public List<? extends HostRoute> getHostRoutes() {
return Lists.newArrayList();
}
@Override
public IPVersionType getIpVersion() {
return null;
}
@Override
public String getGateway() {
return EXPECTED_GATEWAY;
}
@Override
public String getCidr() {
return EXPECTED_CIDR;
}
@Override
public Ipv6AddressMode getIpv6AddressMode() {
return null;
}
@Override
public Ipv6RaMode getIpv6RaMode() {
return null;
}
@Override
public SubnetBuilder toBuilder() {
return null;
}
@Override
public String getTenantId() {
return null;
}
@Override
public void setTenantId(String tenantId) {
}
@Override
public String getName() {
return null;
}
@Override
public void setName(String name) {
}
@Override
public String getId() {
return null;
}
@Override
public void setId(String id) {
}
}
}
/**
* Mocks the OpenstackNodeService.
*/
private class TestOpenstackNodeService extends OpenstackNodeServiceAdapter {
}
/**
* Mocks the OpenstackFlowRuleService.
*/
private class TestOpenstackFlowRuleService extends OpenstackFlowRuleServiceAdapter {
}
/**
* Mocks the instance port.
*/
private final class TestInstancePort implements InstancePort {
@Override
public String networkId() {
return null;
}
@Override
public String portId() {
return null;
}
@Override
public MacAddress macAddress() {
return null;
}
@Override
public IpAddress ipAddress() {
return IpAddress.valueOf(EXPECTED_IP);
}
@Override
public DeviceId deviceId() {
return null;
}
@Override
public DeviceId oldDeviceId() {
return null;
}
@Override
public PortNumber portNumber() {
return null;
}
@Override
public PortNumber oldPortNumber() {
return null;
}
@Override
public State state() {
return null;
}
@Override
public InstancePort updateState(State newState) {
return null;
}
@Override
public InstancePort updatePrevLocation(DeviceId oldDeviceId, PortNumber oldPortNumber) {
return null;
}
}
/**
* Mocks the DefaultPacket context.
*/
private final class TestPacketContext extends DefaultPacketContext {
private TestPacketContext(long time, InboundPacket inPkt,
OutboundPacket outPkt, boolean block) {
super(time, inPkt, outPkt, block);
}
@Override
public void send() {
// We don't send anything out.
}
}
/**
* Keeps a reference to the PacketProcessor and verifies the OutboundPackets.
*/
private class TestPacketService extends PacketServiceAdapter {
@Override
public void addProcessor(PacketProcessor processor, int priority) {
packetProcessor = processor;
}
@Override
public void emit(OutboundPacket packet) {
try {
Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
0, packet.data().array().length);
validatePacket(eth);
} catch (Exception e) {
fail(e.getMessage());
}
}
}
}