diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java b/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java index 7c5564fd6150e6a8d331d9bf039df819bcfb29e0..7c778ac04149b2df330bef1c51cab6fe2b76ae3f 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/IVirtualNetworkService.java @@ -10,7 +10,7 @@ public interface IVirtualNetworkService extends IFloodlightService { * and the fields you want to update. * @param network The network name. Must be unique. * @param guid The ID of the network. Must be unique. - * @param gateway The IP address of the network gateway, null if none. Must be unique. + * @param gateway The IP address of the network gateway, null if none. */ public void createNetwork(String guid, String network, Integer gateway); diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/NetworkResource.java b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/NetworkResource.java index 7faf454d2a4c051a220507f99ca7265c43e539b2..bcead878a8c5f998ee420ee00ada03eef6bcece7 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/NetworkResource.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/NetworkResource.java @@ -56,6 +56,10 @@ public class NetworkResource extends ServerResource { network.name = jp.getText(); } else if (field.equals("gateway")) { network.gateway = jp.getText(); + } else { + log.warn("Unrecognized field {} in " + + "parsing network definition", + jp.getText()); } } } diff --git a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilter.java b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilter.java index 3939aed83185c7ca26c2cc22588731b214c50830..35b0661dc367388d5dfacf38220a65841c0ff22f 100644 --- a/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilter.java +++ b/src/main/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilter.java @@ -33,7 +33,9 @@ import net.floodlightcontroller.core.module.IFloodlightModule; import net.floodlightcontroller.core.module.IFloodlightService; import net.floodlightcontroller.core.util.AppCookie; import net.floodlightcontroller.packet.ARP; +import net.floodlightcontroller.packet.DHCP; import net.floodlightcontroller.packet.Ethernet; +import net.floodlightcontroller.packet.IPacket; import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.util.MACAddress; @@ -48,6 +50,7 @@ import net.floodlightcontroller.virtualnetwork.IVirtualNetworkService; * - This module does not allow overlapping of IPs or MACs * - You can only have 1 gateway per virtual network (can be shared) * - There is filtering of multicast/broadcast traffic + * - All DHCP traffic will be allowed, regardless of unicast/broadcast * * @author alexreimers */ @@ -291,18 +294,33 @@ public class VirtualNetworkFilter return srcNetwork.equals(dstNetwork); } + protected boolean isDhcpPacket(Ethernet frame) { + IPacket payload = frame.getPayload(); // IP + if (payload == null) return false; + IPacket p2 = payload.getPayload(); // TCP or UDP + if (p2 == null) return false; + IPacket p3 = p2.getPayload(); // Application + if ((p3 != null) && (p3 instanceof DHCP)) return true; + return false; + } protected Command processPacketIn(IOFSwitch sw, OFPacketIn msg, FloodlightContext cntx) { Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD); Command ret = Command.STOP; String srcNetwork = macToGuid.get(eth.getSourceMAC()); - if (srcNetwork == null && !(eth.getPayload() instanceof ARP)) { + // If the host is on an unknown network we deny it. + // We make exceptions for ARP in order to handle gateways + // and for DHCP. + if (isDhcpPacket(eth)) { + log.trace("Letting DHCP traffic through"); + ret = Command.CONTINUE; + } else if (srcNetwork == null && !(eth.getPayload() instanceof ARP)) { log.debug("Blocking traffic from host {} because it is not attached to any network.", HexString.toHexString(eth.getSourceMACAddress())); ret = Command.STOP; } else { - if (eth.isBroadcast() || eth.isMulticast()) { + if (eth.isBroadcast() || eth.isMulticast() || (eth.getPayload() instanceof DHCP)) { return Command.CONTINUE; } diff --git a/src/test/java/net/floodlightcontroller/test/util/Packets.java b/src/test/java/net/floodlightcontroller/test/util/Packets.java new file mode 100644 index 0000000000000000000000000000000000000000..da4d6ba10b904ab74184e2d6a1c058256f2fc53a --- /dev/null +++ b/src/test/java/net/floodlightcontroller/test/util/Packets.java @@ -0,0 +1,147 @@ +package net.floodlightcontroller.test.util; + +import java.util.ArrayList; +import java.util.List; + +import net.floodlightcontroller.packet.DHCP; +import net.floodlightcontroller.packet.DHCPOption; +import net.floodlightcontroller.packet.Ethernet; +import net.floodlightcontroller.packet.IPv4; +import net.floodlightcontroller.packet.UDP; +import net.floodlightcontroller.util.MACAddress; + +import org.openflow.protocol.OFPacketIn; +import org.openflow.protocol.OFPacketOut; +import org.openflow.protocol.OFType; +import org.openflow.protocol.OFPacketIn.OFPacketInReason; +import org.openflow.protocol.factory.BasicFactory; + +/** + * A class to that creates many types of L2/L3/L4 or OpenFlow packets. + * This is used in testing. + * @author alexreimers + * + */ +public class Packets { + public static String broadcastMac = "ff:ff:ff:ff:ff:ff"; + public static String broadcastIp = "255.255.255.255"; + protected static BasicFactory OFMessageFactory = new BasicFactory(); + + /** + * Generates a DHCP request OFPacketIn. + * @param hostMac The host MAC address of for the request. + * @return An OFPacketIn that contains a DHCP request packet. + */ + public static OFPacketIn DhcpDiscoveryRequestOFPacketIn(MACAddress hostMac) { + byte[] serializedPacket = DhcpDiscoveryRequestEthernet(hostMac).serialize(); + return (((OFPacketIn)OFMessageFactory + .getMessage(OFType.PACKET_IN)) + .setBufferId(OFPacketOut.BUFFER_ID_NONE) + .setInPort((short) 1) + .setPacketData(serializedPacket) + .setReason(OFPacketInReason.NO_MATCH) + .setTotalLength((short)serializedPacket.length)); + } + + /** + * Generates a DHCP request Ethernet frame. + * @param hostMac The host MAC address of for the request. + * @returnAn An Ethernet frame that contains a DHCP request packet. + */ + public static Ethernet DhcpDiscoveryRequestEthernet(MACAddress hostMac) { + List<DHCPOption> optionList = new ArrayList<DHCPOption>(); + + byte[] requestValue = new byte[4]; + requestValue[0] = requestValue[1] = requestValue[2] = requestValue[3] = 0; + DHCPOption requestOption = + new DHCPOption() + .setCode(DHCP.DHCPOptionCode.OptionCode_RequestedIP. + getValue()) + .setLength((byte)4) + .setData(requestValue); + + byte[] msgTypeValue = new byte[1]; + msgTypeValue[0] = 1; // DHCP request + DHCPOption msgTypeOption = + new DHCPOption() + .setCode(DHCP.DHCPOptionCode.OptionCode_MessageType. + getValue()) + .setLength((byte)1) + .setData(msgTypeValue); + + byte[] reqParamValue = new byte[4]; + reqParamValue[0] = 1; // subnet mask + reqParamValue[1] = 3; // Router + reqParamValue[2] = 6; // Domain Name Server + reqParamValue[3] = 42; // NTP Server + DHCPOption reqParamOption = + new DHCPOption() + .setCode(DHCP.DHCPOptionCode.OptionCode_RequestedParameters. + getValue()) + .setLength((byte)4) + .setData(reqParamValue); + + byte[] clientIdValue = new byte[7]; + clientIdValue[0] = 1; // Ethernet + System.arraycopy(hostMac.toBytes(), 0, + clientIdValue, 1, 6); + DHCPOption clientIdOption = + new DHCPOption() + .setCode(DHCP.DHCPOptionCode.OptionCode_ClientID. + getValue()) + .setLength((byte)7) + .setData(clientIdValue); + + DHCPOption endOption = + new DHCPOption() + .setCode(DHCP.DHCPOptionCode.OptionCode_END. + getValue()) + .setLength((byte)0) + .setData(null); + + optionList.add(requestOption); + optionList.add(msgTypeOption); + optionList.add(reqParamOption); + optionList.add(clientIdOption); + optionList.add(endOption); + + Ethernet requestPacket = new Ethernet(); + requestPacket.setSourceMACAddress(hostMac.toBytes()) + .setDestinationMACAddress(broadcastMac) + .setEtherType(Ethernet.TYPE_IPv4) + .setPayload( + new IPv4() + .setVersion((byte)4) + .setDiffServ((byte)0) + .setIdentification((short)100) + .setFlags((byte)0) + .setFragmentOffset((short)0) + .setTtl((byte)250) + .setProtocol(IPv4.PROTOCOL_UDP) + .setChecksum((short)0) + .setSourceAddress(0) + .setDestinationAddress(broadcastIp) + .setPayload( + new UDP() + .setSourcePort(UDP.DHCP_CLIENT_PORT) + .setDestinationPort(UDP.DHCP_SERVER_PORT) + .setChecksum((short)0) + .setPayload( + new DHCP() + .setOpCode(DHCP.OPCODE_REQUEST) + .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) + .setClientHardwareAddress(hostMac.toBytes()) + .setOptions(optionList)))); + + return requestPacket; + } +} diff --git a/src/test/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilterTest.java b/src/test/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilterTest.java index 34d434412b821bbe2509b09941d00109969ef6cd..26c1ef5823b10989919e4d605b29cdb8cb739cd0 100644 --- a/src/test/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilterTest.java +++ b/src/test/java/net/floodlightcontroller/virtualnetwork/forwarding/VirtualNetworkFilterTest.java @@ -25,6 +25,7 @@ import net.floodlightcontroller.packet.UDP; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.restserver.RestApiServer; import net.floodlightcontroller.test.FloodlightTestCase; +import net.floodlightcontroller.test.util.Packets; import net.floodlightcontroller.util.MACAddress; public class VirtualNetworkFilterTest extends FloodlightTestCase { @@ -64,6 +65,7 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase { protected OFPacketIn mac1ToGwPacketIn; protected IPacket mac1ToGwPacketIntestPacket; protected byte[] mac1ToGwPacketIntestPacketSerialized; + protected OFPacketIn packetInDHCPDiscoveryRequest; @Before public void setUp() throws Exception { @@ -268,7 +270,6 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase { (Ethernet)mac1ToMac2PacketIntestPacket); Command ret = listener.receive(sw1, mac1ToMac2PacketIn, cntx); assertTrue(ret == Command.CONTINUE); - //reset(sw1); // make sure mac1 can't communicate with mac4 cntx = new FloodlightContext(); IFloodlightProviderService.bcStore.put(cntx, @@ -290,4 +291,18 @@ public class VirtualNetworkFilterTest extends FloodlightTestCase { Command ret = listener.receive(sw1, mac1ToGwPacketIn, cntx); assertTrue(ret == Command.CONTINUE); } + + @Test + public void testDhcp() { + IOFMessageListener listener = mockFloodlightProvider.getListeners(). + get(OFType.PACKET_IN).get(0); + Ethernet dhcpPacket = Packets.DhcpDiscoveryRequestEthernet(mac1); + OFPacketIn dhcpPacketOf = Packets.DhcpDiscoveryRequestOFPacketIn(mac1); + cntx = new FloodlightContext(); + IFloodlightProviderService.bcStore.put(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD, + dhcpPacket); + Command ret = listener.receive(sw1, dhcpPacketOf, cntx); + assertTrue(ret == Command.CONTINUE); + } }