Skip to content
Snippets Groups Projects
Commit 804966b7 authored by Ryan Izard's avatar Ryan Izard
Browse files

Initial DHCP server code commit. This was a test of how easy is is to port...

Initial DHCP server code commit. This was a test of how easy is is to port v0.91- code to v1.0. I have not tested this module yet. TODO: Write unit tests for the dhcpserver module.
parent fd4f5b0f
No related branches found
No related tags found
No related merge requests found
package net.floodlightcontroller.dhcpserver;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.MacAddress;
/**
* The class representing a DHCP Binding -- MAC and IP.
* It also contains important information regarding the lease status
* --active
* --inactive
* the lease type of the binding
* --dynamic
* --fixed/static
* and the lease times
* --start time in seconds
* --duration in seconds
*
* @author Ryan Izard (rizard@g.clemson.edu)
*/
public class DHCPBinding {
private MacAddress MAC = MacAddress.NONE;
private IPv4Address IP = IPv4Address.NONE;
private boolean LEASE_STATUS;
private boolean PERMANENT_LEASE;
private long LEASE_START_TIME_SECONDS;
private long LEASE_DURATION_SECONDS;
protected DHCPBinding(IPv4Address ip, MacAddress mac) {
this.setMACAddress(mac);
this.setIPv4Addresss(ip);
this.setLeaseStatus(false);
}
public IPv4Address getIPv4Address() {
return IP;
}
public MacAddress getMACAddress() {
return MAC;
}
private void setIPv4Addresss(IPv4Address ip) {
IP = ip;
}
public void setMACAddress(MacAddress mac) {
MAC = mac;
}
public boolean isActiveLease() {
return LEASE_STATUS;
}
public void setStaticIPLease(boolean staticIP) {
PERMANENT_LEASE = staticIP;
}
public boolean isStaticIPLease() {
return PERMANENT_LEASE;
}
public void setLeaseStatus(boolean status) {
LEASE_STATUS = status;
}
public boolean isLeaseExpired() {
long currentTime = System.currentTimeMillis();
if ((currentTime / 1000) >= (LEASE_START_TIME_SECONDS + LEASE_DURATION_SECONDS)) {
return true;
} else {
return false;
}
}
protected void setLeaseStartTimeSeconds() {
LEASE_START_TIME_SECONDS = System.currentTimeMillis() / 1000;
}
protected void setLeaseDurationSeconds(long time) {
LEASE_DURATION_SECONDS = time;
}
protected void clearLeaseTimes() {
LEASE_START_TIME_SECONDS = 0;
LEASE_DURATION_SECONDS = 0;
}
protected boolean cancelLease() {
this.clearLeaseTimes();
this.setLeaseStatus(false);
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((IP == null) ? 0 : IP.hashCode());
result = prime
* result
+ (int) (LEASE_DURATION_SECONDS ^ (LEASE_DURATION_SECONDS >>> 32));
result = prime
* result
+ (int) (LEASE_START_TIME_SECONDS ^ (LEASE_START_TIME_SECONDS >>> 32));
result = prime * result + (LEASE_STATUS ? 1231 : 1237);
result = prime * result + ((MAC == null) ? 0 : MAC.hashCode());
result = prime * result + (PERMANENT_LEASE ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DHCPBinding other = (DHCPBinding) obj;
if (IP == null) {
if (other.IP != null)
return false;
} else if (!IP.equals(other.IP))
return false;
if (LEASE_DURATION_SECONDS != other.LEASE_DURATION_SECONDS)
return false;
if (LEASE_START_TIME_SECONDS != other.LEASE_START_TIME_SECONDS)
return false;
if (LEASE_STATUS != other.LEASE_STATUS)
return false;
if (MAC == null) {
if (other.MAC != null)
return false;
} else if (!MAC.equals(other.MAC))
return false;
if (PERMANENT_LEASE != other.PERMANENT_LEASE)
return false;
return true;
}
}
\ No newline at end of file
package net.floodlightcontroller.dhcpserver;
import java.util.ArrayList;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.MacAddress;
import org.slf4j.Logger;
import net.floodlightcontroller.dhcpserver.DHCPBinding;
/**
* The class representing a DHCP Pool.
* This class is essentially a list of DHCPBinding objects containing IP, MAC, and lease status information.
*
* @author Ryan Izard (rizard@g.clemson.edu)
*/
public class DHCPPool {
protected Logger log;
private volatile static ArrayList<DHCPBinding> DHCP_POOL = new ArrayList<DHCPBinding>();
private volatile int POOL_SIZE;
private volatile int POOL_AVAILABILITY;
private volatile boolean POOL_FULL;
private volatile IPv4Address STARTING_ADDRESS;
private final MacAddress UNASSIGNED_MAC = MacAddress.NONE;
// Need to write this to handle subnets later...
// This assumes startingIPv4Address can handle size addresses
/**
* Constructor for a DHCPPool of DHCPBinding's. Each DHCPBinding object is initialized with a
* null MAC address and the lease is set to inactive (i.e. false).
* @param {@code byte[]} startingIPv4Address: The lowest IP address to lease.
* @param {@code integer} size: (startingIPv4Address + size) is the highest IP address to lease.
* @return none
*/
public DHCPPool(IPv4Address startingIPv4Address, int size, Logger log) {
this.log = log;
int IPv4AsInt = startingIPv4Address.getInt();
this.setPoolSize(size);
this.setPoolAvailability(size);
STARTING_ADDRESS = startingIPv4Address;
for (int i = 0; i < size; i++) {
DHCP_POOL.add(new DHCPBinding(IPv4Address.of(IPv4AsInt + i), UNASSIGNED_MAC));
}
}
private void setPoolFull(boolean full) {
POOL_FULL = full;
}
private boolean isPoolFull() {
return POOL_FULL;
}
private void setPoolSize(int size) {
POOL_SIZE = size;
}
private int getPoolSize() {
return POOL_SIZE;
}
private int getPoolAvailability() {
return POOL_AVAILABILITY;
}
private void setPoolAvailability(int size) {
POOL_AVAILABILITY = size;
}
/**
* Gets the DHCPBinding object from the DHCPPool containing {@code byte[]} ip
* @param {@code byte[]} ip: The IPv4 address to match in a DHCPBinding
* @return {@code DHCPBinding}: The matching DHCPBinding object or null if ip is not found
*/
public DHCPBinding getDHCPbindingFromIPv4(IPv4Address ip) {
if (ip == null) return null;
for (DHCPBinding binding : DHCP_POOL) {
if (binding.getIPv4Address().equals(ip)) {
return binding;
}
}
return null;
}
/**
* Gets the DHCPBinding object from the DHCPPool containing {@code byte[]} mac
* @param {@code byte[]} mac: The MAC address to match in in a DHCPBinding
* @return {@code DHCPBinding}: The matching DHCPBinding object or null if mac is not found
*/
public DHCPBinding getDHCPbindingFromMAC(MacAddress mac) {
if (mac == null) return null;
for (DHCPBinding binding : DHCP_POOL) {
if (binding.getMACAddress().equals(mac)) {
return binding;
}
}
return null;
}
/**
* Gets the lease status of a particular IPv4 address, {@code byte[]} ip
* @param {@code byte[]} ip: The IPv4 address of which to check the lease status
* @return {@code boolean}: true if lease is active, false if lease is inactive/expired
*/
public boolean isIPv4Leased(IPv4Address ip) {
DHCPBinding binding = this.getDHCPbindingFromIPv4(ip);
if (binding != null) return binding.isActiveLease();
else return false;
}
/**
* Assigns a MAC address to the IP address of the DHCPBinding object in the DHCPPool object.
* This method also sets the lease to active (i.e. true) when the assignment is made.
* @param {@code DHCPBinding} binding: The DHCPBinding object in which to set the MAC
* @param {@code byte[]} mac: The MAC address to set in the DHCPBinding object
* @param {@code long}: The time in seconds for which the lease will be valid
* @return none
*/
public void setDHCPbinding(DHCPBinding binding, MacAddress mac, int time) {
int index = DHCP_POOL.indexOf(binding);
binding.setMACAddress(mac);
binding.setLeaseStatus(true);
this.setPoolAvailability(this.getPoolAvailability() - 1);
DHCP_POOL.set(index, binding);
if (this.getPoolAvailability() == 0) setPoolFull(true);
binding.setLeaseStartTimeSeconds();
binding.setLeaseDurationSeconds(time);
}
/**
* Completely removes the DHCPBinding object with IP address {@code byte[]} ip from the DHCPPool
* @param {@code byte[]} ip: The IP address to remove from the pool. This address will not be available
* for lease after removal.
* @return none
*/
public void removeIPv4FromDHCPPool(IPv4Address ip) {
if (ip == null || getDHCPbindingFromIPv4(ip) == null) return;
if (ip.equals(STARTING_ADDRESS)) {
DHCPBinding lowest = null;
// Locate the lowest address (other than ip), which will be the new starting address
for (DHCPBinding binding : DHCP_POOL) {
if (lowest == null) {
lowest = binding;
} else if (binding.getIPv4Address().getInt() < lowest.getIPv4Address().getInt()
&& !binding.getIPv4Address().equals(ip))
{
lowest = binding;
}
}
// lowest is new starting address
STARTING_ADDRESS = lowest.getIPv4Address();
}
DHCP_POOL.remove(this.getDHCPbindingFromIPv4(ip));
this.setPoolSize(this.getPoolSize() - 1);
this.setPoolAvailability(this.getPoolAvailability() - 1);
if (this.getPoolAvailability() == 0) this.setPoolFull(true);
}
/**
* Adds an IP address to the DHCPPool if the address is not already present. If present, nothing is added to the DHCPPool.
* @param {@code byte[]} ip: The IP address to attempt to add to the DHCPPool
* @return {@code DHCPBinding}: Reference to the DHCPBinding object if successful, null if unsuccessful
*/
public DHCPBinding addIPv4ToDHCPPool(IPv4Address ip) {
DHCPBinding binding = null;
if (this.getDHCPbindingFromIPv4(ip) == null) {
if (ip.getInt() < STARTING_ADDRESS.getInt()) {
STARTING_ADDRESS = ip;
}
binding = new DHCPBinding(ip, null);
DHCP_POOL.add(binding);
this.setPoolSize(this.getPoolSize() + 1);
this.setPoolFull(false);
}
return binding;
}
/**
* Determines if there are available leases in this DHCPPool.
* @return {@code boolean}: true if there are addresses available, false if the DHCPPool is full
*/
public boolean hasAvailableAddresses() {
if (isPoolFull() || getPoolAvailability() == 0) return false;
else return true;
}
/**
* Returns an available address (DHCPBinding) for lease.
* If this MAC is configured for a static/fixed IP, that DHCPBinding will be returned.
* If this MAC has had a lease before and that same lease is available, that DHCPBinding will be returned.
* If not, then an attempt to return an address that has not been active before will be made.
* If there are no addresses that have not been used, then the lowest currently inactive address will be returned.
* If all addresses are being used, then null will be returned.
* @param {@code byte[]): MAC address of the device requesting the lease
* @return {@code DHCPBinding}: Reference to the DHCPBinding object if successful, null if unsuccessful
*/
public DHCPBinding getAnyAvailableLease(MacAddress mac) {
if (isPoolFull()) return null;
DHCPBinding usedBinding = null;
usedBinding = this.getDHCPbindingFromMAC(mac);
if (usedBinding != null) return usedBinding;
for (DHCPBinding binding : DHCP_POOL) {
if (!binding.isActiveLease()
&& binding.getMACAddress().equals(UNASSIGNED_MAC))
{
return binding;
} else if (!binding.isActiveLease() && usedBinding == null && !binding.isStaticIPLease()) {
usedBinding = binding;
}
}
return usedBinding;
}
/**
* Returns a specific available IP address binding for lease. The MAC and IP will be queried
* against the DHCP pool. (1) If the MAC is found in an available, fixed binding, and that binding
* is not for the provided IP, the fixed binding associated with the MAC will be returned. (2) If the
* IP is found in an available, fixed binding, and that binding also contains the MAC address provided,
* then the binding will be returned -- this is true only if the IP and MAC result in the same available,
* fixed binding. (3) If the IP is found in the pool and it is available and not fixed, then its
* binding will be returned. (4) If the IP provided does not match any available entries or is invalid,
* null will be returned. If this is the case, run getAnyAvailableLease(mac) to resolve.
* @param {@code byte[]}: The IP address on which to try and obtain a lease
* @param {@code byte[]}: The MAC address on which to try and obtain a lease.
* @return {@code DHCPBinding}: Reference to the DHCPBinding object if successful, null if unsuccessful.
*/
public DHCPBinding getSpecificAvailableLease(IPv4Address ip, MacAddress mac) {
if (ip == null || mac == null || isPoolFull()) return null;
DHCPBinding binding = this.getDHCPbindingFromIPv4(ip);
DHCPBinding binding2 = this.getDHCPbindingFromMAC(mac);
// For all of the following, the binding is also determined to be inactive:
// If configured, we must return a fixed binding for a MAC address even if it's requesting another IP
if (binding2 != null && !binding2.isActiveLease() && binding2.isStaticIPLease() && binding != binding2) {
if (log != null) log.info("Fixed DHCP entry for MAC trumps requested IP. Returning binding for MAC");
return binding2;
// If configured, we must return a fixed binding for an IP if the binding is fixed to the provided MAC (ideal static request case)
} else if (binding != null && !binding.isActiveLease() && binding.isStaticIPLease() && mac.equals(binding.getMACAddress())) {
if (log != null) log.info("Found matching fixed DHCP entry for IP with MAC. Returning binding for IP with MAC");
return binding;
// The IP and MAC are not a part of a fixed binding, so return the binding of the requested IP
} else if (binding != null && !binding.isActiveLease() && !binding.isStaticIPLease()) {
if (log != null) log.info("No fixed DHCP entry for IP or MAC found. Returning dynamic binding for IP.");
return binding;
// Otherwise, the binding is fixed for both MAC and IP and this MAC does not match either, so we can't return it as available
} else {
if (log != null) log.debug("Invalid IP address request or IP is actively leased...check for any available lease to resolve");
return null;
}
}
/**
* Tries to renew an IP lease.
* @param {@code byte[]}: The IP address on which to try and renew a lease
* @param {@code long}: The time in seconds for which the lease will be valid
* @return {@code DHCPBinding}: True on success, false if unknown IP address
*/
public boolean renewLease(IPv4Address ip, int time) {
DHCPBinding binding = this.getDHCPbindingFromIPv4(ip);
if (binding != null) {
binding.setLeaseStartTimeSeconds();
binding.setLeaseDurationSeconds(time);
binding.setLeaseStatus(true);
return true;
}
return false;
}
/**
* Cancel an IP lease.
* @param {@code byte[]}: The IP address on which to try and cancel a lease
* @return {@code boolean}: True on success, false if unknown IP address
*/
public boolean cancelLeaseOfIPv4(IPv4Address ip) {
DHCPBinding binding = this.getDHCPbindingFromIPv4(ip);
if (binding != null) {
binding.clearLeaseTimes();
binding.setLeaseStatus(false);
this.setPoolAvailability(this.getPoolAvailability() + 1);
this.setPoolFull(false);
return true;
}
return false;
}
/**
* Cancel an IP lease.
* @param {@code byte[]}: The MAC address on which to try and cancel a lease
* @return {@code boolean}: True on success, false if unknown IP address
*/
public boolean cancelLeaseOfMAC(MacAddress mac) {
DHCPBinding binding = getDHCPbindingFromMAC(mac);
if (binding != null) {
binding.clearLeaseTimes();
binding.setLeaseStatus(false);
this.setPoolAvailability(this.getPoolAvailability() + 1);
this.setPoolFull(false);
return true;
}
return false;
}
/**
* Make the addresses of expired leases available and reset the lease times.
* @return {@code ArrayList<DHCPBinding>}: A list of the bindings that are now available
*/
public ArrayList<DHCPBinding> cleanExpiredLeases() {
ArrayList<DHCPBinding> newAvailableLeases = new ArrayList<DHCPBinding>();
for (DHCPBinding binding : DHCP_POOL) {
// isLeaseExpired() automatically excludes configured static leases
if (binding.isLeaseExpired() && binding.isActiveLease()) {
this.cancelLeaseOfIPv4(binding.getIPv4Address());
this.setPoolAvailability(this.getPoolAvailability() + 1);
this.setPoolFull(false);
newAvailableLeases.add(binding);
}
}
return newAvailableLeases;
}
/**
* Used to set a particular IP binding in the pool as a fixed/static IP lease.
* This method does not set the lease as active, but instead reserves that IP
* for only the MAC provided. To set the lease as active, the methods getAnyAvailableLease()
* or getSpecificAvailableLease() will return the correct binding given the same
* MAC provided to this method is used to bind the lease later on.
* @param {@code byte[]}: The IP address to set as static/fixed.
* @param {@code byte[]}: The MAC address to match to the IP address ip when
* an address is requested from the MAC mac
* @return {@code boolean}: True upon success; false upon failure (e.g. no IP found)
*/
public boolean configureFixedIPLease(IPv4Address ip, MacAddress mac) {
DHCPBinding binding = this.getDHCPbindingFromIPv4(ip);
if (binding != null) {
binding.setMACAddress(mac);
binding.setStaticIPLease(true);
binding.setLeaseStatus(false);
return true;
} else {
return false;
}
}
}
\ No newline at end of file
package net.floodlightcontroller.dhcpserver;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.projectfloodlight.openflow.protocol.OFMessage;
import org.projectfloodlight.openflow.protocol.OFPacketIn;
import org.projectfloodlight.openflow.protocol.OFPacketOut;
import org.projectfloodlight.openflow.protocol.OFType;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.types.EthType;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.IpProtocol;
import org.projectfloodlight.openflow.types.MacAddress;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.floodlightcontroller.core.FloodlightContext;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFMessageListener;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.internal.IOFSwitchService;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.forwarding.Forwarding;
import net.floodlightcontroller.packet.DHCP.DHCPOptionCode;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.packet.UDP;
import net.floodlightcontroller.packet.DHCP;
import net.floodlightcontroller.packet.DHCPOption;
/**
* SDN DHCP Server
* @author Ryan Izard, rizard@g.clemson.edu
*
*
* The Floodlight Module implementing a DHCP DHCPServer.
* This module uses {@code DHCPPool} to manage DHCP leases.
* It intercepts any DHCP/BOOTP requests from connected hosts and
* handles the replies. The configuration file:
*
* floodlight/src/main/resources/floodlightdefault.properties
*
* contains the DHCP options and parameters that can be set. To allow
* all DHCP request messages to be sent to the controller (Floodlight),
* the DHCPSwitchFlowSetter module (in this same package) and the
* Forwarding module (loaded by default) should also be loaded in
* Floodlight. When the first DHCP request is received on a particular
* port of an OpenFlow switch, the request will by default be sent to
* the control plane to the controller for processing. The DHCPServer
* module will intercept the message before it makes it to the Forwarding
* module and process the packet. Now, because we don't want to hog all
* the DHCP messages (in case there is another module that is using them)
* we forward the packets down to other modules using Command.CONTINUE.
* As a side effect, the forwarding module will insert flows in the OF
* switch for our DHCP traffic even though we've already processed it.
* In order to allow all future DHCP messages from that same port to be
* sent to the controller (and not follow the Forwarding module's flows),
* we need to proactively insert flows for all DHCP client traffic on
* UDP port 67 to the controller. These flows will allow all DHCP traffic
* to be intercepted on that same port and sent to the DHCP server running
* on the Floodlight controller.
*
* Currently, this DHCP server only supports a single subnet; however,
* work is ongoing to use connected OF switches and ports to allow
* the user to configure multiple subnets. On a traditional DHCP server,
* the machine is configured with different NICs, each with their own
* statically-assigned IP address/subnet/mask. The DHCP server matches
* the network information of each NIC with the DHCP server's configured
* subnets and answers the requests accordingly. To mirror this behavior
* on a OpenFlow network, we can differentiate between subnets based on a
* device's attachment point. We can assign subnets for a device per
* OpenFlow switch or per port per switch. This is the next step for
* this implementations of a SDN DHCP server.
*
* I welcome any feedback or suggestions for improvement!
*
*
*/
public class DHCPServer implements IOFMessageListener, IFloodlightModule {
protected static Logger log;
protected static IFloodlightProviderService floodlightProvider;
protected static IOFSwitchService switchService;
// The garbage collector service for the DHCP server
// Handle expired leases by adding the IP back to the address pool
private static ScheduledThreadPoolExecutor leasePoliceDispatcher;
//private static ScheduledFuture<?> leasePoliceOfficer;
private static Runnable leasePolicePatrol;
// Contains the pool of IP addresses their bindings to MAC addresses
// Tracks the lease status and duration of DHCP bindings
private static volatile DHCPPool theDHCPPool;
/** START CONFIG FILE VARIABLES **/
// These variables are set using the floodlightdefault.properties file
// Refer to startup() for a list of the expected names in the config file
// The IP and MAC addresses of the controller/DHCP server
private static MacAddress CONTROLLER_MAC;
private static IPv4Address CONTROLLER_IP;
private static IPv4Address DHCP_SERVER_DHCP_SERVER_IP; // Same as CONTROLLER_IP but in byte[] form
private static IPv4Address DHCP_SERVER_SUBNET_MASK;
private static IPv4Address DHCP_SERVER_BROADCAST_IP;
private static IPv4Address DHCP_SERVER_IP_START;
private static IPv4Address DHCP_SERVER_IP_STOP;
private static int DHCP_SERVER_ADDRESS_SPACE_SIZE; // Computed in startUp()
private static IPv4Address DHCP_SERVER_ROUTER_IP = null;
private static byte[] DHCP_SERVER_NTP_IP_LIST = null;
private static byte[] DHCP_SERVER_DNS_IP_LIST = null;
private static byte[] DHCP_SERVER_DN = null;
private static byte[] DHCP_SERVER_IP_FORWARDING = null;
private static int DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS;
private static int DHCP_SERVER_HOLD_LEASE_TIME_SECONDS;
private static int DHCP_SERVER_REBIND_TIME_SECONDS; // Computed in startUp()
private static int DHCP_SERVER_RENEWAL_TIME_SECONDS; // Computed in startUp()
private static long DHCP_SERVER_LEASE_POLICE_PATROL_PERIOD_SECONDS;
/** END CONFIG FILE VARIABLES **/
/**
* DHCP messages are either:
* REQUEST (client --0x01--> server)
* or REPLY (server --0x02--> client)
*/
public static byte DHCP_OPCODE_REQUEST = intToBytes(1)[0];
public static byte DHCP_OPCODE_REPLY = intToBytes(2)[0];
/**
* DHCP REQUEST messages are either of type:
* DISCOVER (0x01)
* REQUEST (0x03)
* DECLINE (0x04)
* RELEASE (0x07)
* or INFORM (0x08)
* DHCP REPLY messages are either of type:
* OFFER (0x02)
* ACK (0x05)
* or NACK (0x06)
**/
public static byte[] DHCP_MSG_TYPE_DISCOVER = intToBytesSizeOne(1);
public static byte[] DHCP_MSG_TYPE_OFFER = intToBytesSizeOne(2);
public static byte[] DHCP_MSG_TYPE_REQUEST = intToBytesSizeOne(3);
public static byte[] DHCP_MSG_TYPE_DECLINE = intToBytesSizeOne(4);
public static byte[] DHCP_MSG_TYPE_ACK = intToBytesSizeOne(5);
public static byte[] DHCP_MSG_TYPE_NACK = intToBytesSizeOne(6);
public static byte[] DHCP_MSG_TYPE_RELEASE = intToBytesSizeOne(7);
public static byte[] DHCP_MSG_TYPE_INFORM = intToBytesSizeOne(8);
/**
* DHCP messages contain options requested by the client and
* provided by the server. The options requested by the client are
* provided in a list (option 0x37 below) and the server elects to
* answer some or all of these options and may provide additional
* options as necessary for the DHCP client to obtain a lease.
* OPTION NAME HEX DEC
* Subnet Mask 0x01 1
* Router IP 0x03 3
* DNS Server IP 0x06 6
* Domain Name 0x0F 15
* IP Forwarding 0x13 19
* Broadcast IP 0x1C 28
* NTP Server IP 0x2A 42
* NetBios Name IP 0x2C 44
* NetBios DDS IP 0x2D 45
* NetBios Node Type 0x2E 46
* NetBios Scope ID 0x2F 47
* Requested IP 0x32 50
* Lease Time (s) 0x33 51
* Msg Type (above) 0x35 53
* DHCP Server IP 0x36 54
* Option List (this) 0x37 55
* Renewal Time (s) 0x3A 58
* Rebind Time (s) 0x3B 59
* End Option List 0xFF 255
*
* NetBios options are not currently implemented in this server but can be added
* via the configuration file.
**/
public static byte DHCP_REQ_PARAM_OPTION_CODE_SN = intToBytes(1)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_ROUTER = intToBytes(3)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_DNS = intToBytes(6)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_DN = intToBytes(15)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING = intToBytes(19)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP = intToBytes(28)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_NTP_IP = intToBytes(42)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_NET_BIOS_NAME_IP = intToBytes(44)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_NET_BIOS_DDS_IP = intToBytes(45)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_NET_BIOS_NODE_TYPE = intToBytes(46)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_NET_BIOS_SCOPE_ID = intToBytes(47)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_IP = intToBytes(50)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME = intToBytes(51)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_MSG_TYPE = intToBytes(53)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER = intToBytes(54)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_PARAMTERS = intToBytes(55)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME = intToBytes(58)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME = intToBytes(59)[0];
public static byte DHCP_REQ_PARAM_OPTION_CODE_END = intToBytes(255)[0];
// Used for composing DHCP REPLY messages
public static final MacAddress BROADCAST_MAC = MacAddress.BROADCAST;
public static final IPv4Address BROADCAST_IP = IPv4Address.NO_MASK; /* no_mask is all 1's */
public static final IPv4Address UNASSIGNED_IP = IPv4Address.FULL_MASK; /* full_mask is all 0's */
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
return l;
}
@Override
public void init(FloodlightModuleContext context) throws FloodlightModuleException {
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
switchService = context.getServiceImpl(IOFSwitchService.class);
log = LoggerFactory.getLogger(DHCPServer.class);
}
@Override
public void startUp(FloodlightModuleContext context) {
floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this);
// Read our config options for the DHCP DHCPServer
Map<String, String> configOptions = context.getConfigParams(this);
try {
DHCP_SERVER_SUBNET_MASK = IPv4Address.of(configOptions.get("subnet-mask"));
DHCP_SERVER_IP_START = IPv4Address.of(configOptions.get("lower-ip-range"));
DHCP_SERVER_IP_STOP = IPv4Address.of(configOptions.get("upper-ip-range"));
DHCP_SERVER_ADDRESS_SPACE_SIZE = DHCP_SERVER_IP_STOP.getInt() - DHCP_SERVER_IP_START.getInt() + 1;
DHCP_SERVER_BROADCAST_IP = IPv4Address.of(configOptions.get("broadcast-address"));
DHCP_SERVER_ROUTER_IP = IPv4Address.of(configOptions.get("router"));
DHCP_SERVER_DN = configOptions.get("domain-name").getBytes();
DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS = Integer.parseInt(configOptions.get("default-lease-time"));
DHCP_SERVER_HOLD_LEASE_TIME_SECONDS = Integer.parseInt(configOptions.get("hold-lease-time"));
DHCP_SERVER_RENEWAL_TIME_SECONDS = (int) (DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS / 2.0);
DHCP_SERVER_REBIND_TIME_SECONDS = (int) (DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS * 0.875);
DHCP_SERVER_LEASE_POLICE_PATROL_PERIOD_SECONDS = Long.parseLong(configOptions.get("lease-gc-period"));
DHCP_SERVER_IP_FORWARDING = intToBytesSizeOne(Integer.parseInt(configOptions.get("ip-forwarding")));
CONTROLLER_MAC = MacAddress.of(configOptions.get("controller-mac"));
CONTROLLER_IP = IPv4Address.of(configOptions.get("controller-ip"));
DHCP_SERVER_DHCP_SERVER_IP = CONTROLLER_IP;
// NetBios and other options can be added to this function here as needed in the future
} catch(IllegalArgumentException ex) {
log.error("Incorrect DHCP Server configuration options", ex);
throw ex;
} catch(NullPointerException ex) {
log.error("Incorrect DHCP Server configuration options", ex);
throw ex;
}
// Create our new DHCPPool object with the specific address size
theDHCPPool = new DHCPPool(DHCP_SERVER_IP_START, DHCP_SERVER_ADDRESS_SPACE_SIZE, log);
// Any addresses that need to be set as static/fixed can be permanently added to the pool with a set MAC
String staticAddresses = configOptions.get("reserved-static-addresses");
if (staticAddresses != null) {
String[] macIpCouples = staticAddresses.split("\\s*;\\s*");
int i;
String[] macIpSplit;
int ipPos, macPos;
for (i = 0; i < macIpCouples.length; i++) {
macIpSplit = macIpCouples[i].split("\\s*,\\s*");
// Determine which element is the MAC and which is the IP
// i.e. which order have they been typed in in the config file?
if (macIpSplit[0].length() > macIpSplit[1].length()) {
macPos = 0;
ipPos = 1;
} else {
macPos = 1;
ipPos = 0;
}
if (theDHCPPool.configureFixedIPLease(IPv4Address.of(macIpSplit[ipPos]), MacAddress.of(macIpSplit[macPos]))) {
String ip = theDHCPPool.getDHCPbindingFromIPv4(IPv4Address.of(macIpSplit[ipPos])).getIPv4Address().toString();
String mac = theDHCPPool.getDHCPbindingFromIPv4(IPv4Address.of(macIpSplit[ipPos])).getMACAddress().toString();
log.info("Configured fixed address of " + ip + " for device " + mac);
} else {
log.error("Could not configure fixed address " + macIpSplit[ipPos] + " for device " + macIpSplit[macPos]);
}
}
}
// The order of the DNS and NTP servers should be most reliable to least
String dnses = configOptions.get("domain-name-servers");
String ntps = configOptions.get("ntp-servers");
// Separate the servers in the comma-delimited list
// TODO If the list is null then we need to not include this information with the options request,
// otherwise the client will get incorrect option information
if (dnses != null) {
DHCP_SERVER_DNS_IP_LIST = IPv4.toIPv4AddressBytes(dnses.split("\\s*,\\s*")[0].toString());
}
if (ntps != null) {
DHCP_SERVER_NTP_IP_LIST = IPv4.toIPv4AddressBytes(ntps.split("\\s*,\\s*")[0].toString());
}
// Monitor bindings for expired leases and clean them up
leasePoliceDispatcher = new ScheduledThreadPoolExecutor(1);
leasePolicePatrol = new DHCPLeasePolice();
/*leasePoliceOfficer = */
leasePoliceDispatcher.scheduleAtFixedRate(leasePolicePatrol, 10,
DHCP_SERVER_LEASE_POLICE_PATROL_PERIOD_SECONDS, TimeUnit.SECONDS);
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
return null;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
return null;
}
@Override
public String getName() {
return DHCPServer.class.getSimpleName();
}
@Override
public boolean isCallbackOrderingPrereq(OFType type, String name) {
return false;
}
@Override
public boolean isCallbackOrderingPostreq(OFType type, String name) {
// We will rely on forwarding to forward out any DHCP packets that are not
// destined for our DHCP server. This is to allow an environment where
// multiple DHCP servers operate cooperatively
if (type == OFType.PACKET_IN && name.equals(Forwarding.class.getSimpleName())) {
return true;
} else {
return false;
}
}
public static byte[] intToBytes(int integer) {
byte[] bytes = new byte[4];
bytes[3] = (byte) (integer >> 24);
bytes[2] = (byte) (integer >> 16);
bytes[1] = (byte) (integer >> 8);
bytes[0] = (byte) (integer);
return bytes;
}
public static byte[] intToBytesSizeOne(int integer) {
byte[] bytes = new byte[1];
bytes[0] = (byte) (integer);
return bytes;
}
public void sendDHCPOffer(IOFSwitch sw, OFPort inPort, MacAddress chaddr, IPv4Address dstIPAddr,
IPv4Address yiaddr, IPv4Address giaddr, int xid, ArrayList<Byte> requestOrder) {
// Compose DHCP OFFER
/** (2) DHCP Offer
* -- UDP src port = 67
* -- UDP dst port = 68
* -- IP src addr = DHCP DHCPServer's IP
* -- IP dst addr = 255.255.255.255
* -- Opcode = 0x02
* -- XID = transactionX
* -- ciaddr = blank
* -- yiaddr = offer IP
* -- siaddr = DHCP DHCPServer IP
* -- giaddr = blank
* -- chaddr = Client's MAC
* -- Options:
* -- Option 53 = DHCP Offer
* -- Option 1 = SN Mask IP
* -- Option 3 = Router IP
* -- Option 51 = Lease time (s)
* -- Option 54 = DHCP DHCPServer IP
* -- Option 6 = DNS servers
**/
OFPacketOut.Builder DHCPOfferPacket = sw.getOFFactory().buildPacketOut();
DHCPOfferPacket.setBufferId(OFBufferId.NO_BUFFER);
Ethernet ethDHCPOffer = new Ethernet();
ethDHCPOffer.setSourceMACAddress(CONTROLLER_MAC);
ethDHCPOffer.setDestinationMACAddress(chaddr);
ethDHCPOffer.setEtherType(EthType.IPv4);
IPv4 ipv4DHCPOffer = new IPv4();
if (dstIPAddr.equals(IPv4Address.NONE)) {
ipv4DHCPOffer.setDestinationAddress(BROADCAST_IP);
} else { // Client has IP and dhcpc must have crashed
ipv4DHCPOffer.setDestinationAddress(dstIPAddr);
}
ipv4DHCPOffer.setSourceAddress(CONTROLLER_IP);
ipv4DHCPOffer.setProtocol(IpProtocol.UDP);
ipv4DHCPOffer.setTtl((byte) 64);
UDP udpDHCPOffer = new UDP();
udpDHCPOffer.setDestinationPort(UDP.DHCP_CLIENT_PORT);
udpDHCPOffer.setSourcePort(UDP.DHCP_SERVER_PORT);
DHCP dhcpDHCPOffer = new DHCP();
dhcpDHCPOffer.setOpCode(DHCP_OPCODE_REPLY);
dhcpDHCPOffer.setHardwareType((byte) 1);
dhcpDHCPOffer.setHardwareAddressLength((byte) 6);
dhcpDHCPOffer.setHops((byte) 0);
dhcpDHCPOffer.setTransactionId(xid);
dhcpDHCPOffer.setSeconds((short) 0);
dhcpDHCPOffer.setFlags((short) 0);
dhcpDHCPOffer.setClientIPAddress(UNASSIGNED_IP);
dhcpDHCPOffer.setYourIPAddress(yiaddr);
dhcpDHCPOffer.setServerIPAddress(CONTROLLER_IP);
dhcpDHCPOffer.setGatewayIPAddress(giaddr);
dhcpDHCPOffer.setClientHardwareAddress(chaddr);
List<DHCPOption> dhcpOfferOptions = new ArrayList<DHCPOption>();
DHCPOption newOption;
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_MSG_TYPE);
newOption.setData(DHCP_MSG_TYPE_OFFER);
newOption.setLength((byte) 1);
dhcpOfferOptions.add(newOption);
for (Byte specificRequest : requestOrder) {
if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_SN) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_SN);
newOption.setData(DHCP_SERVER_SUBNET_MASK.getBytes());
newOption.setLength((byte) 4);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_ROUTER) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_ROUTER);
newOption.setData(DHCP_SERVER_ROUTER_IP.getBytes());
newOption.setLength((byte) 4);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DN) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DN);
newOption.setData(DHCP_SERVER_DN);
newOption.setLength((byte) DHCP_SERVER_DN.length);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DNS) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DNS);
newOption.setData(DHCP_SERVER_DNS_IP_LIST);
newOption.setLength((byte) DHCP_SERVER_DNS_IP_LIST.length);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP);
newOption.setData(DHCP_SERVER_BROADCAST_IP.getBytes());
newOption.setLength((byte) 4);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER);
newOption.setData(DHCP_SERVER_DHCP_SERVER_IP.getBytes());
newOption.setLength((byte) 4);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME);
newOption.setData(intToBytes(DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS));
newOption.setLength((byte) 4);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_NTP_IP) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_NTP_IP);
newOption.setData(DHCP_SERVER_NTP_IP_LIST);
newOption.setLength((byte) DHCP_SERVER_NTP_IP_LIST.length);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME);
newOption.setData(intToBytes(DHCP_SERVER_REBIND_TIME_SECONDS));
newOption.setLength((byte) 4);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME);
newOption.setData(intToBytes(DHCP_SERVER_RENEWAL_TIME_SECONDS));
newOption.setLength((byte) 4);
dhcpOfferOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING);
newOption.setData(DHCP_SERVER_IP_FORWARDING);
newOption.setLength((byte) 1);
dhcpOfferOptions.add(newOption);
} else {
//log.debug("Setting specific request for OFFER failed");
}
}
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_END);
newOption.setLength((byte) 0);
dhcpOfferOptions.add(newOption);
dhcpDHCPOffer.setOptions(dhcpOfferOptions);
ethDHCPOffer.setPayload(ipv4DHCPOffer.setPayload(udpDHCPOffer.setPayload(dhcpDHCPOffer)));
DHCPOfferPacket.setInPort(OFPort.ANY);
List<OFAction> actions = new ArrayList<OFAction>(1);
actions.add(sw.getOFFactory().actions().output(inPort, 0xffFFffFF));
DHCPOfferPacket.setActions(actions);
DHCPOfferPacket.setData(ethDHCPOffer.serialize());
log.debug("Sending DHCP OFFER");
sw.write(DHCPOfferPacket.build());
}
public void sendDHCPAck(IOFSwitch sw, OFPort inPort, MacAddress chaddr, IPv4Address dstIPAddr,
IPv4Address yiaddr, IPv4Address giaddr, int xid, ArrayList<Byte> requestOrder) {
/** (4) DHCP ACK
* -- UDP src port = 67
* -- UDP dst port = 68
* -- IP src addr = DHCP DHCPServer's IP
* -- IP dst addr = 255.255.255.255
* -- Opcode = 0x02
* -- XID = transactionX
* -- ciaddr = blank
* -- yiaddr = offer IP
* -- siaddr = DHCP DHCPServer IP
* -- giaddr = blank
* -- chaddr = Client's MAC
* -- Options:
* -- Option 53 = DHCP ACK
* -- Option 1 = SN Mask IP
* -- Option 3 = Router IP
* -- Option 51 = Lease time (s)
* -- Option 54 = DHCP DHCPServer IP
* -- Option 6 = DNS servers
**/
OFPacketOut.Builder DHCPACKPacket = sw.getOFFactory().buildPacketOut();
DHCPACKPacket.setBufferId(OFBufferId.NO_BUFFER);
Ethernet ethDHCPAck = new Ethernet();
ethDHCPAck.setSourceMACAddress(CONTROLLER_MAC);
ethDHCPAck.setDestinationMACAddress(chaddr);
ethDHCPAck.setEtherType(EthType.IPv4);
IPv4 ipv4DHCPAck = new IPv4();
if (dstIPAddr.equals(IPv4Address.NONE)) {
ipv4DHCPAck.setDestinationAddress(BROADCAST_IP);
} else { // Client has IP and dhclient must have crashed
ipv4DHCPAck.setDestinationAddress(dstIPAddr);
}
ipv4DHCPAck.setSourceAddress(CONTROLLER_IP);
ipv4DHCPAck.setProtocol(IpProtocol.UDP);
ipv4DHCPAck.setTtl((byte) 64);
UDP udpDHCPAck = new UDP();
udpDHCPAck.setDestinationPort(UDP.DHCP_CLIENT_PORT);
udpDHCPAck.setSourcePort(UDP.DHCP_SERVER_PORT);
DHCP dhcpDHCPAck = new DHCP();
dhcpDHCPAck.setOpCode(DHCP_OPCODE_REPLY);
dhcpDHCPAck.setHardwareType((byte) 1);
dhcpDHCPAck.setHardwareAddressLength((byte) 6);
dhcpDHCPAck.setHops((byte) 0);
dhcpDHCPAck.setTransactionId(xid);
dhcpDHCPAck.setSeconds((short) 0);
dhcpDHCPAck.setFlags((short) 0);
dhcpDHCPAck.setClientIPAddress(UNASSIGNED_IP);
dhcpDHCPAck.setYourIPAddress(yiaddr);
dhcpDHCPAck.setServerIPAddress(CONTROLLER_IP);
dhcpDHCPAck.setGatewayIPAddress(giaddr);
dhcpDHCPAck.setClientHardwareAddress(chaddr);
List<DHCPOption> dhcpAckOptions = new ArrayList<DHCPOption>();
DHCPOption newOption;
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_MSG_TYPE);
newOption.setData(DHCP_MSG_TYPE_ACK);
newOption.setLength((byte) 1);
dhcpAckOptions.add(newOption);
for (Byte specificRequest : requestOrder) {
if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_SN) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_SN);
newOption.setData(DHCP_SERVER_SUBNET_MASK.getBytes());
newOption.setLength((byte) 4);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_ROUTER) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_ROUTER);
newOption.setData(DHCP_SERVER_ROUTER_IP.getBytes());
newOption.setLength((byte) 4);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DN) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DN);
newOption.setData(DHCP_SERVER_DN);
newOption.setLength((byte) DHCP_SERVER_DN.length);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DNS) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DNS);
newOption.setData(DHCP_SERVER_DNS_IP_LIST);
newOption.setLength((byte) DHCP_SERVER_DNS_IP_LIST.length);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP);
newOption.setData(DHCP_SERVER_BROADCAST_IP.getBytes());
newOption.setLength((byte) 4);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER);
newOption.setData(DHCP_SERVER_DHCP_SERVER_IP.getBytes());
newOption.setLength((byte) 4);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME);
newOption.setData(intToBytes(DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS));
newOption.setLength((byte) 4);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_NTP_IP) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_NTP_IP);
newOption.setData(DHCP_SERVER_NTP_IP_LIST);
newOption.setLength((byte) DHCP_SERVER_NTP_IP_LIST.length);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME);
newOption.setData(intToBytes(DHCP_SERVER_REBIND_TIME_SECONDS));
newOption.setLength((byte) 4);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME);
newOption.setData(intToBytes(DHCP_SERVER_RENEWAL_TIME_SECONDS));
newOption.setLength((byte) 4);
dhcpAckOptions.add(newOption);
} else if (specificRequest.byteValue() == DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING) {
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING);
newOption.setData(DHCP_SERVER_IP_FORWARDING);
newOption.setLength((byte) 1);
dhcpAckOptions.add(newOption);
}else {
log.debug("Setting specific request for ACK failed");
}
}
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_END);
newOption.setLength((byte) 0);
dhcpAckOptions.add(newOption);
dhcpDHCPAck.setOptions(dhcpAckOptions);
ethDHCPAck.setPayload(ipv4DHCPAck.setPayload(udpDHCPAck.setPayload(dhcpDHCPAck)));
DHCPACKPacket.setInPort(OFPort.ANY);
List<OFAction> actions = new ArrayList<OFAction>(1);
actions.add(sw.getOFFactory().actions().output(inPort, 0xffFFffFF));
DHCPACKPacket.setActions(actions);
DHCPACKPacket.setData(ethDHCPAck.serialize());
log.debug("Sending DHCP ACK");
sw.write(DHCPACKPacket.build());
}
public void sendDHCPNack(IOFSwitch sw, OFPort inPort, MacAddress chaddr, IPv4Address giaddr, int xid) {
OFPacketOut.Builder DHCPOfferPacket = sw.getOFFactory().buildPacketOut();
DHCPOfferPacket.setBufferId(OFBufferId.NO_BUFFER);
Ethernet ethDHCPOffer = new Ethernet();
ethDHCPOffer.setSourceMACAddress(CONTROLLER_MAC);
ethDHCPOffer.setDestinationMACAddress(chaddr);
ethDHCPOffer.setEtherType(EthType.IPv4);
IPv4 ipv4DHCPOffer = new IPv4();
ipv4DHCPOffer.setDestinationAddress(BROADCAST_IP);
ipv4DHCPOffer.setSourceAddress(CONTROLLER_IP);
ipv4DHCPOffer.setProtocol(IpProtocol.UDP);
ipv4DHCPOffer.setTtl((byte) 64);
UDP udpDHCPOffer = new UDP();
udpDHCPOffer.setDestinationPort(UDP.DHCP_CLIENT_PORT);
udpDHCPOffer.setSourcePort(UDP.DHCP_SERVER_PORT);
DHCP dhcpDHCPOffer = new DHCP();
dhcpDHCPOffer.setOpCode(DHCP_OPCODE_REPLY);
dhcpDHCPOffer.setHardwareType((byte) 1);
dhcpDHCPOffer.setHardwareAddressLength((byte) 6);
dhcpDHCPOffer.setHops((byte) 0);
dhcpDHCPOffer.setTransactionId(xid);
dhcpDHCPOffer.setSeconds((short) 0);
dhcpDHCPOffer.setFlags((short) 0);
dhcpDHCPOffer.setClientIPAddress(UNASSIGNED_IP);
dhcpDHCPOffer.setYourIPAddress(UNASSIGNED_IP);
dhcpDHCPOffer.setServerIPAddress(CONTROLLER_IP);
dhcpDHCPOffer.setGatewayIPAddress(giaddr);
dhcpDHCPOffer.setClientHardwareAddress(chaddr);
List<DHCPOption> dhcpOfferOptions = new ArrayList<DHCPOption>();
DHCPOption newOption;
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_MSG_TYPE);
newOption.setData(DHCP_MSG_TYPE_NACK);
newOption.setLength((byte) 1);
dhcpOfferOptions.add(newOption);
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER);
newOption.setData(DHCP_SERVER_DHCP_SERVER_IP.getBytes());
newOption.setLength((byte) 4);
dhcpOfferOptions.add(newOption);
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_END);
newOption.setLength((byte) 0);
dhcpOfferOptions.add(newOption);
dhcpDHCPOffer.setOptions(dhcpOfferOptions);
ethDHCPOffer.setPayload(ipv4DHCPOffer.setPayload(udpDHCPOffer.setPayload(dhcpDHCPOffer)));
DHCPOfferPacket.setInPort(OFPort.ANY);
List<OFAction> actions = new ArrayList<OFAction>(1);
actions.add(sw.getOFFactory().actions().output(inPort, 0xffFFffFF));
DHCPOfferPacket.setActions(actions);
DHCPOfferPacket.setData(ethDHCPOffer.serialize());
log.info("Sending DHCP NACK");
sw.write(DHCPOfferPacket.build());
}
public ArrayList<Byte> getRequestedParameters(DHCP DHCPPayload, boolean isInform) {
ArrayList<Byte> requestOrder = new ArrayList<Byte>();
byte[] requests = DHCPPayload.getOption(DHCPOptionCode.OptionCode_RequestedParameters).getData();
boolean requestedLeaseTime = false;
boolean requestedRebindTime = false;
boolean requestedRenewTime = false;
for (byte specificRequest : requests) {
if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_SN) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_SN);
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_ROUTER) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_ROUTER);
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_DN) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_DN);
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_DNS) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_DNS);
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME);
requestedLeaseTime = true;
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER);
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_BROADCAST_IP);
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_NTP_IP) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_NTP_IP);
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME);
requestedRebindTime = true;
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME);
requestedRenewTime = true;
} else if (specificRequest == DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_IP_FORWARDING);
log.debug("requested IP FORWARDING");
} else {
//log.debug("Requested option 0x" + Byte.toString(specificRequest) + " not available");
}
}
// We need to add these in regardless if the request list includes them
if (!isInform) {
if (!requestedLeaseTime) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_LEASE_TIME);
log.debug("added option LEASE TIME");
}
if (!requestedRenewTime) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_RENEWAL_TIME);
log.debug("added option RENEWAL TIME");
}
if (!requestedRebindTime) {
requestOrder.add(DHCP_REQ_PARAM_OPTION_CODE_REBIND_TIME);
log.debug("added option REBIND TIME");
}
}
return requestOrder;
}
@Override
public net.floodlightcontroller.core.IListener.Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) {
OFPacketIn pi = (OFPacketIn) msg;
if (!theDHCPPool.hasAvailableAddresses()) {
log.info("DHCP Pool is full! Consider increasing the pool size.");
return Command.CONTINUE;
}
Ethernet eth = IFloodlightProviderService.bcStore.get(cntx,
IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
if (eth.getEtherType() == EthType.IPv4) { /* shallow compare is okay for EthType */
log.debug("Got IPv4 Packet");
IPv4 IPv4Payload = (IPv4) eth.getPayload();
IPv4Address IPv4SrcAddr = IPv4Payload.getSourceAddress();
if (IPv4Payload.getProtocol() == IpProtocol.UDP) { /* shallow compare also okay for IpProtocol */
log.debug("Got UDP Packet");
UDP UDPPayload = (UDP) IPv4Payload.getPayload();
if ((UDPPayload.getDestinationPort().equals(UDP.DHCP_SERVER_PORT) /* TransportPort must be deep though */
|| UDPPayload.getDestinationPort().equals(UDP.DHCP_CLIENT_PORT))
&& (UDPPayload.getSourcePort().equals(UDP.DHCP_SERVER_PORT)
|| UDPPayload.getSourcePort().equals(UDP.DHCP_CLIENT_PORT)))
{
log.debug("Got DHCP Packet");
// This is a DHCP packet that we need to process
DHCP DHCPPayload = (DHCP) UDPPayload.getPayload();
OFPort inPort = pi.getInPort();
/* DHCP/IPv4 Header Information */
int xid = 0;
IPv4Address yiaddr = IPv4Address.NONE;
IPv4Address giaddr = IPv4Address.NONE;
MacAddress chaddr = null;
IPv4Address desiredIPAddr = null;
ArrayList<Byte> requestOrder = new ArrayList<Byte>();
if (DHCPPayload.getOpCode() == DHCP_OPCODE_REQUEST) {
/** * (1) DHCP Discover
* -- UDP src port = 68
* -- UDP dst port = 67
* -- IP src addr = 0.0.0.0
* -- IP dst addr = 255.255.255.255
* -- Opcode = 0x01
* -- XID = transactionX
* -- All addresses blank:
* -- ciaddr (client IP)
* -- yiaddr (your IP)
* -- siaddr (DHCPServer IP)
* -- giaddr (GW IP)
* -- chaddr = Client's MAC
* -- Options:
* -- Option 53 = DHCP Discover
* -- Option 50 = possible IP request
* -- Option 55 = parameter request list
* -- (1) SN Mask
* -- (3) Router
* -- (15) Domain Name
* -- (6) DNS
**/
if (Arrays.equals(DHCPPayload.getOption(DHCP.DHCPOptionCode.OptionCode_MessageType).getData(), DHCP_MSG_TYPE_DISCOVER)) {
log.debug("DHCP DISCOVER Received");
xid = DHCPPayload.getTransactionId();
yiaddr = DHCPPayload.getYourIPAddress();
// Will have GW IP if a relay agent was used
giaddr = DHCPPayload.getGatewayIPAddress();
chaddr = DHCPPayload.getClientHardwareAddress();
List<DHCPOption> options = DHCPPayload.getOptions();
for (DHCPOption option : options) {
if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_IP) {
desiredIPAddr = IPv4Address.of(option.getData());
log.debug("Got requested IP");
} else if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_PARAMTERS) {
log.debug("Got requested param list");
requestOrder = getRequestedParameters(DHCPPayload, false);
}
}
// Process DISCOVER message and prepare an OFFER with minimum-hold lease
// A HOLD lease should be a small amount of time sufficient for the client to respond
// with a REQUEST, at which point the ACK will set the least time to the DEFAULT
synchronized (theDHCPPool) {
if (!theDHCPPool.hasAvailableAddresses()) {
log.info("DHCP Pool is full! Consider increasing the pool size.");
log.info("Device with MAC " + chaddr.toString() + " was not granted an IP lease");
return Command.CONTINUE;
}
DHCPBinding lease = theDHCPPool.getSpecificAvailableLease(desiredIPAddr, chaddr);
if (lease != null) {
log.debug("Checking new lease with specific IP");
theDHCPPool.setDHCPbinding(lease, chaddr, DHCP_SERVER_HOLD_LEASE_TIME_SECONDS);
yiaddr = lease.getIPv4Address();
log.debug("Got new lease for " + yiaddr.toString());
} else {
log.debug("Checking new lease for any IP");
lease = theDHCPPool.getAnyAvailableLease(chaddr);
theDHCPPool.setDHCPbinding(lease, chaddr, DHCP_SERVER_HOLD_LEASE_TIME_SECONDS);
yiaddr = lease.getIPv4Address();
log.debug("Got new lease for " + yiaddr.toString());
}
}
sendDHCPOffer(sw, inPort, chaddr, IPv4SrcAddr, yiaddr, giaddr, xid, requestOrder);
} // END IF DISCOVER
/** (3) DHCP Request
* -- UDP src port = 68
* -- UDP dst port = 67
* -- IP src addr = 0.0.0.0
* -- IP dst addr = 255.255.255.255
* -- Opcode = 0x01
* -- XID = transactionX
* -- ciaddr = blank
* -- yiaddr = blank
* -- siaddr = DHCP DHCPServer IP
* -- giaddr = GW IP
* -- chaddr = Client's MAC
* -- Options:
* -- Option 53 = DHCP Request
* -- Option 50 = IP requested (from offer)
* -- Option 54 = DHCP DHCPServer IP
**/
else if (Arrays.equals(DHCPPayload.getOption(DHCP.DHCPOptionCode.OptionCode_MessageType).getData(), DHCP_MSG_TYPE_REQUEST)) {
log.debug(": DHCP REQUEST received");
IPv4SrcAddr = IPv4Payload.getSourceAddress();
xid = DHCPPayload.getTransactionId();
yiaddr = DHCPPayload.getYourIPAddress();
giaddr = DHCPPayload.getGatewayIPAddress();
chaddr = DHCPPayload.getClientHardwareAddress();
List<DHCPOption> options = DHCPPayload.getOptions();
for (DHCPOption option : options) {
if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_IP) {
desiredIPAddr = IPv4Address.of(option.getData());
if (!desiredIPAddr.equals(theDHCPPool.getDHCPbindingFromMAC(chaddr).getIPv4Address())) {
// This client wants a different IP than what we have on file, so cancel its HOLD lease now (if we have one)
theDHCPPool.cancelLeaseOfMAC(chaddr);
return Command.CONTINUE;
}
} else if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER) {
if (!IPv4Address.of(option.getData()).equals(DHCP_SERVER_DHCP_SERVER_IP)) {
// We're not the DHCPServer the client wants to use, so cancel its HOLD lease now and ignore the client
theDHCPPool.cancelLeaseOfMAC(chaddr);
return Command.CONTINUE;
}
} else if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_PARAMTERS) {
requestOrder = getRequestedParameters(DHCPPayload, false);
}
}
// Process REQUEST message and prepare an ACK with default lease time
// This extends the hold lease time to that of a normal lease
boolean sendACK = true;
synchronized (theDHCPPool) {
if (!theDHCPPool.hasAvailableAddresses()) {
log.info("DHCP Pool is full! Consider increasing the pool size.");
log.info("Device with MAC " + chaddr.toString() + " was not granted an IP lease");
return Command.CONTINUE;
}
DHCPBinding lease;
// Get any binding, in use now or not
if (desiredIPAddr != null) {
lease = theDHCPPool.getDHCPbindingFromIPv4(desiredIPAddr);
} else {
lease = theDHCPPool.getAnyAvailableLease(chaddr);
}
// This IP is not in our allocation range
if (lease == null) {
log.info("The IP " + desiredIPAddr.toString() + " is not in the range "
+ DHCP_SERVER_IP_START.toString() + " to " + DHCP_SERVER_IP_STOP.toString());
log.info("Device with MAC " + chaddr.toString() + " was not granted an IP lease");
sendACK = false;
// Determine if the IP in the binding we just retrieved is okay to allocate to the MAC requesting it
} else if (!lease.getMACAddress().equals(chaddr) && lease.isActiveLease()) {
log.debug("Tried to REQUEST an IP that is currently assigned to another MAC");
log.debug("Device with MAC " + chaddr.toString() + " was not granted an IP lease");
sendACK = false;
// Check if we want to renew the MAC's current lease
} else if (lease.getMACAddress().equals(chaddr) && lease.isActiveLease()) {
log.debug("Renewing lease for MAC " + chaddr.toString());
theDHCPPool.renewLease(lease.getIPv4Address(), DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS);
yiaddr = lease.getIPv4Address();
log.debug("Finalized renewed lease for " + yiaddr.toString());
// Check if we want to create a new lease for the MAC
} else if (!lease.isActiveLease()){
log.debug("Assigning new lease for MAC " + chaddr.toString());
theDHCPPool.setDHCPbinding(lease, chaddr, DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS);
yiaddr = lease.getIPv4Address();
log.debug("Finalized new lease for " + yiaddr.toString());
} else {
log.debug("Don't know how we got here");
return Command.CONTINUE;
}
}
if (sendACK) {
sendDHCPAck(sw, inPort, chaddr, IPv4SrcAddr, yiaddr, giaddr, xid, requestOrder);
} else {
sendDHCPNack(sw, inPort, chaddr, giaddr, xid);
}
} // END IF REQUEST
else if (Arrays.equals(DHCPPayload.getOption(DHCP.DHCPOptionCode.OptionCode_MessageType).getData(), DHCP_MSG_TYPE_RELEASE)) {
if (DHCPPayload.getServerIPAddress() != CONTROLLER_IP) {
log.info("DHCP RELEASE message not for our DHCP server");
// Send the packet out the port it would normally go out via the Forwarding module
// Execution jumps to return Command.CONTINUE at end of receive()
} else {
log.debug("Got DHCP RELEASE. Cancelling remaining time on DHCP lease");
synchronized(theDHCPPool) {
if (theDHCPPool.cancelLeaseOfMAC(DHCPPayload.getClientHardwareAddress())) {
log.info("Cancelled DHCP lease of " + DHCPPayload.getClientHardwareAddress().toString());
log.info("IP " + theDHCPPool.getDHCPbindingFromMAC(DHCPPayload.getClientHardwareAddress()).getIPv4Address().toString()
+ " is now available in the DHCP address pool");
} else {
log.debug("Lease of " + DHCPPayload.getClientHardwareAddress().toString()
+ " was already inactive");
}
}
}
} // END IF RELEASE
else if (Arrays.equals(DHCPPayload.getOption(DHCP.DHCPOptionCode.OptionCode_MessageType).getData(), DHCP_MSG_TYPE_DECLINE)) {
log.debug("Got DHCP DECLINE. Cancelling HOLD time on DHCP lease");
synchronized(theDHCPPool) {
if (theDHCPPool.cancelLeaseOfMAC(DHCPPayload.getClientHardwareAddress())) {
log.info("Cancelled DHCP lease of " + DHCPPayload.getClientHardwareAddress().toString());
log.info("IP " + theDHCPPool.getDHCPbindingFromMAC(DHCPPayload.getClientHardwareAddress()).getIPv4Address().toString()
+ " is now available in the DHCP address pool");
} else {
log.info("HOLD Lease of " + DHCPPayload.getClientHardwareAddress().toString()
+ " has already expired");
}
}
} // END IF DECLINE
else if (Arrays.equals(DHCPPayload.getOption(DHCP.DHCPOptionCode.OptionCode_MessageType).getData(), DHCP_MSG_TYPE_INFORM)) {
log.debug("Got DHCP INFORM. Retreiving requested parameters from message");
IPv4SrcAddr = IPv4Payload.getSourceAddress();
xid = DHCPPayload.getTransactionId();
yiaddr = DHCPPayload.getYourIPAddress();
giaddr = DHCPPayload.getGatewayIPAddress();
chaddr = DHCPPayload.getClientHardwareAddress();
// Get the requests from the INFORM message. True for inform -- we don't want to include lease information
requestOrder = getRequestedParameters(DHCPPayload, true);
// Process INFORM message and send an ACK with requested information
sendDHCPAck(sw, inPort, chaddr, IPv4SrcAddr, yiaddr, giaddr, xid, requestOrder);
} // END IF INFORM
} // END IF DHCP OPCODE REQUEST
else if (DHCPPayload.getOpCode() == DHCP_OPCODE_REPLY) {
// Do nothing right now. The DHCP DHCPServer isn't supposed to receive replies but ISSUE them instead
log.debug("Got an OFFER/ACK (REPLY)...this shouldn't happen unless there's another DHCP Server somewhere");
} else {
log.debug("Got DHCP packet, but not a known DHCP packet opcode");
}
} // END IF DHCP packet
} // END IF UDP packet
} // END IF IPv4 packet
return Command.CONTINUE;
} // END of receive(pkt)
/**
* DHCPLeasePolice is a simple class that is instantiated and invoked
* as a runnable thread. The objective is to clean up the expired DHCP
* leases on a set time interval. Most DHCP leases are hours in length,
* so the granularity of our check can be on the order of minutes (IMHO).
* The period of the check for expired leases, in seconds, is specified
* in the configuration file:
*
* floodlight/src/main/resources/floodlightdefault.properties
*
* as option:
*
* net.floodlightcontroller.dhcpserver.DHCPServer.lease-gc-period = <seconds>
*
* where gc stands for "garbage collection".
*
* @author Ryan Izard, rizard@g.clemson.edu
*
*/
class DHCPLeasePolice implements Runnable {
@Override
public void run() {
log.info("Cleaning any expired DHCP leases...");
ArrayList<DHCPBinding> newAvailableBindings;
synchronized(theDHCPPool) {
// Loop through lease pool and check all leases to see if they are expired
// If a lease is expired, then clean it up and make the binding available
newAvailableBindings = theDHCPPool.cleanExpiredLeases();
}
for (DHCPBinding binding : newAvailableBindings) {
log.info("MAC " + binding.getMACAddress().toString() + " has expired");
log.info("Lease now available for IP " + binding.getIPv4Address().toString());
}
}
} // END DHCPLeasePolice Class
} // END DHCPServer Class
\ No newline at end of file
package net.floodlightcontroller.dhcpserver;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import org.projectfloodlight.openflow.protocol.OFFlowAdd;
import org.projectfloodlight.openflow.protocol.OFPortDesc;
import org.projectfloodlight.openflow.protocol.action.OFAction;
import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
import org.projectfloodlight.openflow.protocol.match.Match;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.DatapathId;
import org.projectfloodlight.openflow.types.EthType;
import org.projectfloodlight.openflow.types.IpProtocol;
import org.projectfloodlight.openflow.types.OFBufferId;
import org.projectfloodlight.openflow.types.OFPort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.floodlightcontroller.core.IFloodlightProviderService;
import net.floodlightcontroller.core.IOFSwitch;
import net.floodlightcontroller.core.IOFSwitchListener;
import net.floodlightcontroller.core.PortChangeType;
import net.floodlightcontroller.core.internal.IOFSwitchService;
import net.floodlightcontroller.core.module.FloodlightModuleContext;
import net.floodlightcontroller.core.module.FloodlightModuleException;
import net.floodlightcontroller.core.module.IFloodlightModule;
import net.floodlightcontroller.core.module.IFloodlightService;
import net.floodlightcontroller.packet.UDP;
import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
// Adding a comment to test a new commit
public class DHCPSwitchFlowSetter implements IFloodlightModule, IOFSwitchListener {
protected static Logger log;
protected IFloodlightProviderService floodlightProvider;
protected IStaticFlowEntryPusherService sfp;
protected IOFSwitchService switchService;
@Override
public Collection<Class<? extends IFloodlightService>> getModuleServices() {
return null;
}
@Override
public Map<Class<? extends IFloodlightService>, IFloodlightService> getServiceImpls() {
return null;
}
@Override
public Collection<Class<? extends IFloodlightService>> getModuleDependencies() {
Collection<Class<? extends IFloodlightService>> l =
new ArrayList<Class<? extends IFloodlightService>>();
l.add(IFloodlightProviderService.class);
l.add(IStaticFlowEntryPusherService.class);
l.add(IOFSwitchService.class);
return l;
}
@Override
public void init(FloodlightModuleContext context)
throws FloodlightModuleException {
floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class);
log = LoggerFactory.getLogger(DHCPServer.class);
sfp = context.getServiceImpl(IStaticFlowEntryPusherService.class);
switchService = context.getServiceImpl(IOFSwitchService.class);
}
@Override
public void startUp(FloodlightModuleContext context) {
}
@Override
public void switchAdded(DatapathId dpid) {
/* Insert static flows on all ports of the switch to redirect
* DHCP client --> DHCP DHCPServer traffic to the controller.
* DHCP client's operate on UDP port 67
*/
IOFSwitch sw = switchService.getSwitch(dpid);
OFFlowAdd.Builder flow = sw.getOFFactory().buildFlowAdd();
Match.Builder match = sw.getOFFactory().buildMatch();
ArrayList<OFAction> actionList = new ArrayList<OFAction>();
OFActionOutput.Builder action = sw.getOFFactory().actions().buildOutput();
for (OFPortDesc port : sw.getPorts()) {
match.setExact(MatchField.IN_PORT, port.getPortNo());
match.setExact(MatchField.ETH_TYPE, EthType.IPv4);
match.setExact(MatchField.IP_PROTO, IpProtocol.UDP);
match.setExact(MatchField.UDP_SRC, UDP.DHCP_CLIENT_PORT);
action.setMaxLen(0xffFFffFF);
action.setPort(OFPort.CONTROLLER);
actionList.add(action.build());
flow.setBufferId(OFBufferId.NO_BUFFER);
flow.setHardTimeout(0);
flow.setIdleTimeout(0);
flow.setOutPort(OFPort.CONTROLLER);
flow.setActions(actionList);
flow.setMatch(match.build());
flow.setPriority(32767);
sfp.addFlow("dhcp-port---" + port.getPortNo().getPortNumber() + "---(" + port.getName() + ")", flow.build(), sw.getId());
}
}
@Override
public void switchRemoved(DatapathId switchId) {
}
@Override
public void switchActivated(DatapathId switchId) {
}
@Override
public void switchPortChanged(DatapathId switchId, OFPortDesc port, PortChangeType type) {
}
@Override
public void switchChanged(DatapathId switchId) {
}
}
\ No newline at end of file
......@@ -23,6 +23,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.MacAddress;
/**
*
* @author David Erickson (daviderickson@cs.stanford.edu)
......@@ -92,11 +95,11 @@ public class DHCP extends BasePacket {
protected int transactionId;
protected short seconds;
protected short flags;
protected int clientIPAddress;
protected int yourIPAddress;
protected int serverIPAddress;
protected int gatewayIPAddress;
protected byte[] clientHardwareAddress;
protected IPv4Address clientIPAddress;
protected IPv4Address yourIPAddress;
protected IPv4Address serverIPAddress;
protected IPv4Address gatewayIPAddress;
protected MacAddress clientHardwareAddress;
protected String serverName;
protected String bootFileName;
protected List<DHCPOption> options = new ArrayList<DHCPOption>();
......@@ -209,14 +212,14 @@ public class DHCP extends BasePacket {
/**
* @return the clientIPAddress
*/
public int getClientIPAddress() {
public IPv4Address getClientIPAddress() {
return clientIPAddress;
}
/**
* @param clientIPAddress the clientIPAddress to set
*/
public DHCP setClientIPAddress(int clientIPAddress) {
public DHCP setClientIPAddress(IPv4Address clientIPAddress) {
this.clientIPAddress = clientIPAddress;
return this;
}
......@@ -224,14 +227,14 @@ public class DHCP extends BasePacket {
/**
* @return the yourIPAddress
*/
public int getYourIPAddress() {
public IPv4Address getYourIPAddress() {
return yourIPAddress;
}
/**
* @param yourIPAddress the yourIPAddress to set
*/
public DHCP setYourIPAddress(int yourIPAddress) {
public DHCP setYourIPAddress(IPv4Address yourIPAddress) {
this.yourIPAddress = yourIPAddress;
return this;
}
......@@ -239,14 +242,14 @@ public class DHCP extends BasePacket {
/**
* @return the serverIPAddress
*/
public int getServerIPAddress() {
public IPv4Address getServerIPAddress() {
return serverIPAddress;
}
/**
* @param serverIPAddress the serverIPAddress to set
*/
public DHCP setServerIPAddress(int serverIPAddress) {
public DHCP setServerIPAddress(IPv4Address serverIPAddress) {
this.serverIPAddress = serverIPAddress;
return this;
}
......@@ -254,14 +257,14 @@ public class DHCP extends BasePacket {
/**
* @return the gatewayIPAddress
*/
public int getGatewayIPAddress() {
public IPv4Address getGatewayIPAddress() {
return gatewayIPAddress;
}
/**
* @param gatewayIPAddress the gatewayIPAddress to set
*/
public DHCP setGatewayIPAddress(int gatewayIPAddress) {
public DHCP setGatewayIPAddress(IPv4Address gatewayIPAddress) {
this.gatewayIPAddress = gatewayIPAddress;
return this;
}
......@@ -269,14 +272,14 @@ public class DHCP extends BasePacket {
/**
* @return the clientHardwareAddress
*/
public byte[] getClientHardwareAddress() {
public MacAddress getClientHardwareAddress() {
return clientHardwareAddress;
}
/**
* @param clientHardwareAddress the clientHardwareAddress to set
*/
public DHCP setClientHardwareAddress(byte[] clientHardwareAddress) {
public DHCP setClientHardwareAddress(MacAddress clientHardwareAddress) {
this.clientHardwareAddress = clientHardwareAddress;
return this;
}
......@@ -381,13 +384,13 @@ public class DHCP extends BasePacket {
bb.putInt(this.transactionId);
bb.putShort(this.seconds);
bb.putShort(this.flags);
bb.putInt(this.clientIPAddress);
bb.putInt(this.yourIPAddress);
bb.putInt(this.serverIPAddress);
bb.putInt(this.gatewayIPAddress);
bb.put(this.clientHardwareAddress);
if (this.clientHardwareAddress.length < 16) {
for (int i = 0; i < (16 - this.clientHardwareAddress.length); ++i) {
bb.putInt(this.clientIPAddress.getInt());
bb.putInt(this.yourIPAddress.getInt());
bb.putInt(this.serverIPAddress.getInt());
bb.putInt(this.gatewayIPAddress.getInt());
bb.put(this.clientHardwareAddress.getBytes());
if (this.clientHardwareAddress.getLength() < 16) {
for (int i = 0; i < (16 - this.clientHardwareAddress.getLength()); ++i) {
bb.put((byte) 0x0);
}
}
......@@ -447,14 +450,14 @@ public class DHCP extends BasePacket {
this.transactionId = bb.getInt();
this.seconds = bb.getShort();
this.flags = bb.getShort();
this.clientIPAddress = bb.getInt();
this.yourIPAddress = bb.getInt();
this.serverIPAddress = bb.getInt();
this.gatewayIPAddress = bb.getInt();
this.clientIPAddress = IPv4Address.of(bb.getInt());
this.yourIPAddress = IPv4Address.of(bb.getInt());
this.serverIPAddress = IPv4Address.of(bb.getInt());
this.gatewayIPAddress = IPv4Address.of(bb.getInt());
int hardwareAddressLength = 0xff & this.hardwareAddressLength;
this.clientHardwareAddress = new byte[hardwareAddressLength];
bb.get(this.clientHardwareAddress);
byte[] tmpMac = new byte[hardwareAddressLength];
bb.get(tmpMac);
this.clientHardwareAddress = MacAddress.of(tmpMac); /* the assumption here is that we only have MAC address HW addresses */
for (int i = hardwareAddressLength; i < 16; ++i)
bb.get();
this.serverName = readString(bb, 64);
......
......@@ -27,6 +27,7 @@ import org.projectfloodlight.openflow.protocol.OFPacketInReason;
import org.projectfloodlight.openflow.protocol.OFVersion;
import org.projectfloodlight.openflow.protocol.match.MatchField;
import org.projectfloodlight.openflow.types.EthType;
import org.projectfloodlight.openflow.types.IPv4Address;
import org.projectfloodlight.openflow.types.IpProtocol;
import org.projectfloodlight.openflow.types.MacAddress;
import org.projectfloodlight.openflow.types.OFPort;
......@@ -163,11 +164,11 @@ public class PacketFactory {
.setTransactionId(0x00003d1d)
.setSeconds((short)0)
.setFlags((short)0)
.setClientIPAddress(0)
.setYourIPAddress(0)
.setServerIPAddress(0)
.setGatewayIPAddress(0)
.setClientHardwareAddress(hostMac.getBytes())
.setClientIPAddress(IPv4Address.NONE)
.setYourIPAddress(IPv4Address.NONE)
.setServerIPAddress(IPv4Address.NONE)
.setGatewayIPAddress(IPv4Address.NONE)
.setClientHardwareAddress(hostMac)
.setOptions(optionList))));
return requestPacket;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment