package net.floodlightcontroller.dhcpserver;
import java.io.IOException;
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.openflow.protocol.OFFlowMod;
import org.openflow.protocol.OFMatch;
import org.openflow.protocol.OFMessage;
import org.openflow.protocol.OFPacketIn;
import org.openflow.protocol.OFPacketOut;
import org.openflow.protocol.OFPhysicalPort;
import org.openflow.protocol.OFPort;
import org.openflow.protocol.OFType;
import org.openflow.protocol.action.OFAction;
import org.openflow.protocol.action.OFActionDataLayerDestination;
import org.openflow.protocol.action.OFActionDataLayerSource;
import org.openflow.protocol.action.OFActionOutput;
import org.openflow.protocol.action.OFActionType;
import org.openflow.protocol.statistics.OFDescriptionStatistics;
import org.openflow.util.HexString;
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.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.ARP;
import net.floodlightcontroller.packet.DHCP.DHCPOptionCode;
import net.floodlightcontroller.packet.Data;
import net.floodlightcontroller.packet.Ethernet;
import net.floodlightcontroller.packet.IPacket;
import net.floodlightcontroller.packet.IPv4;
import net.floodlightcontroller.packet.UDP;
import net.floodlightcontroller.packet.DHCP;
import net.floodlightcontroller.packet.DHCPOption;
import net.floodlightcontroller.staticflowentry.IStaticFlowEntryPusherService;
import net.floodlightcontroller.util.MACAddress;
/**
* 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 IFloodlightProviderService floodlightProvider;
protected IStaticFlowEntryPusherService sfp;
// 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 byte[] CONTROLLER_MAC;
private static int CONTROLLER_IP;
private static byte[] DHCP_SERVER_DHCP_SERVER_IP; // Same as CONTROLLER_IP but in byte[] form
private static byte[] DHCP_SERVER_SUBNET_MASK;
private static byte[] DHCP_SERVER_BROADCAST_IP;
private static byte[] DHCP_SERVER_IP_START;
private static byte[] DHCP_SERVER_IP_STOP;
private static int DHCP_SERVER_ADDRESS_SPACE_SIZE; // Computed in startUp()
private static byte[] DHCP_SERVER_ROUTER_IP = null;
private static byte[] DHCP_SERVER_ROUTER_MAC = 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;
private static int ROOT_NODE_ROOT_OVS_IP;
private static String ROOT_NODE_WIMAX_OVS_DPID;
private static int WIFI_NODE_IP;
private static String WIFI_NODE_WIFI_OVS_DPID;
private static String ROOT_NODE_ROOT_OVS_DPID;
private static short ROOT_NODE_ROOT_OVS_WIFI_PATCH;
private static short ROOT_NODE_ROOT_OVS_WIMAX_PATCH;
/** 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 byte[] BROADCAST_MAC = Ethernet.toMACAddress("FF:FF:FF:FF:FF:FF");
public static final int BROADCAST_IP = IPv4.toIPv4Address(IPv4.toIPv4AddressBytes("255.255.255.255"));
public static final int UNASSIGNED_IP = IPv4.toIPv4Address(IPv4.toIPv4AddressBytes("0.0.0.0"));
@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);
sfp = context.getServiceImpl(IStaticFlowEntryPusherService.class);
log = LoggerFactory.getLogger(DHCPServer.class);
return;
}
@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 = IPv4.toIPv4AddressBytes(configOptions.get("subnet-mask"));
DHCP_SERVER_IP_START = IPv4.toIPv4AddressBytes(configOptions.get("lower-ip-range"));
DHCP_SERVER_IP_STOP = IPv4.toIPv4AddressBytes(configOptions.get("upper-ip-range"));
DHCP_SERVER_ADDRESS_SPACE_SIZE = IPv4.toIPv4Address(DHCP_SERVER_IP_STOP) - IPv4.toIPv4Address(DHCP_SERVER_IP_START) + 1;
DHCP_SERVER_BROADCAST_IP = IPv4.toIPv4AddressBytes(configOptions.get("broadcast-address"));
DHCP_SERVER_ROUTER_IP = IPv4.toIPv4AddressBytes(configOptions.get("router"));
DHCP_SERVER_ROUTER_MAC = Ethernet.toMACAddress(configOptions.get("router-mac"));
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 = Ethernet.toMACAddress(configOptions.get("controller-mac"));
CONTROLLER_IP = IPv4.toIPv4Address(configOptions.get("controller-ip"));
DHCP_SERVER_DHCP_SERVER_IP = IPv4.toIPv4AddressBytes(CONTROLLER_IP);
ROOT_NODE_ROOT_OVS_DPID = configOptions.get("root-node-root-ovs-dpid");
ROOT_NODE_WIMAX_OVS_DPID = configOptions.get("root-node-wimax-ovs-dpid");
WIFI_NODE_WIFI_OVS_DPID = configOptions.get("wifi-node-wifi-ovs-dpid");
ROOT_NODE_ROOT_OVS_WIFI_PATCH = Short.parseShort(configOptions.get("root-node-root-ovs-wifi-patch-port"));
ROOT_NODE_ROOT_OVS_WIMAX_PATCH = Short.parseShort(configOptions.get("root-node-root-ovs-wimax-patch-port"));
ROOT_NODE_ROOT_OVS_IP = IPv4.toIPv4Address(configOptions.get("root-node-root-ovs-ip"));
WIFI_NODE_IP = IPv4.toIPv4Address(configOptions.get("wifi-node-ip"));
// NetBios and other options can be added to this function here as needed in the future
} catch(IllegalArgumentException e) {
log.error("Incorrect DHCP Server configuration options", e);
throw e;
} catch(NullPointerException e) {
log.error("Incorrect DHCP Server configuration options", e);
throw e;
}
// 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[] macIpBindings = staticAddresses.split("\\s*;\\s*");
String[] macIpSplit;
int ipPos = -1;
ArrayList<byte[]> macs = new ArrayList<byte[]>();
for (int i = 0; i < macIpBindings.length; i++) {
macIpSplit = macIpBindings[i].split("\\s*,\\s*");
// Determine which elements are the MACs and which is the IP
// i.e. which order have they been typed in in the config file?
//log.info(macIpSplit[0] + " " + macIpSplit[1]);
for (int j = 0; j < macIpSplit.length; j++) {
if (macIpSplit[j].length() <= DHCPBinding.IP_ADDRESS_STRING_LENGTH_MAX && macIpSplit[j].length() >= DHCPBinding.IP_ADDRESS_STRING_LENGTH_MIN) {
ipPos = j;
} else {
macs.add(Ethernet.toMACAddress(macIpSplit[j]));
}
}
if (ipPos != -1 && theDHCPPool.configureFixedIPLease(IPv4.toIPv4AddressBytes(macIpSplit[ipPos]), macs)) {
String ip = theDHCPPool.getDHCPbindingFromIPv4(IPv4.toIPv4AddressBytes(macIpSplit[ipPos])).getIPv4AddresString();
String mac = theDHCPPool.getDHCPbindingFromIPv4(IPv4.toIPv4AddressBytes(macIpSplit[ipPos])).getMACAddressesString();
log.info("Configured fixed address of " + ip + " for device " + mac);
} else {
log.error("Could not configure fixed address " + macIpSplit[ipPos] + " for device!");
}
macs.clear();
}
return;
}
// 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, short inPort, byte[] chaddr, int dstIPAddr,
int yiaddr, int 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 DHCPOfferPacket = (OFPacketOut)
floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
DHCPOfferPacket.setBufferId(-1);
Ethernet ethDHCPOffer = new Ethernet();
ethDHCPOffer.setSourceMACAddress(CONTROLLER_MAC);
ethDHCPOffer.setDestinationMACAddress(chaddr);
ethDHCPOffer.setEtherType(Ethernet.TYPE_IPv4);
IPv4 ipv4DHCPOffer = new IPv4();
if (dstIPAddr == 0) {
ipv4DHCPOffer.setDestinationAddress(BROADCAST_IP);
} else { // Client has IP and dhcpc must have crashed
ipv4DHCPOffer.setDestinationAddress(dstIPAddr);
}
ipv4DHCPOffer.setSourceAddress(CONTROLLER_IP);
ipv4DHCPOffer.setProtocol(IPv4.PROTOCOL_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);
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);
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);
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");
}
}
// Doing this as a test for the Teltonika
newOption = new DHCPOption();
newOption.setCode(DHCP_REQ_PARAM_OPTION_CODE_DHCP_SERVER);
newOption.setData(DHCP_SERVER_DHCP_SERVER_IP);
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)));
short packetOutLength = (short) OFPacketOut.MINIMUM_LENGTH;
DHCPOfferPacket.setInPort(OFPort.OFPP_NONE.getValue());
DHCPOfferPacket.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
packetOutLength = (short) (packetOutLength + OFActionOutput.MINIMUM_LENGTH);
List<OFAction> actions = new ArrayList<OFAction>(1);
actions.add(new OFActionOutput(inPort, (short) 0));
DHCPOfferPacket.setActions(actions);
DHCPOfferPacket.setPacketData(ethDHCPOffer.serialize());
packetOutLength = (short) (packetOutLength + ethDHCPOffer.serialize().length);
DHCPOfferPacket.setLength(packetOutLength);
log.debug("Sending DHCP OFFER");
try {
sw.write(DHCPOfferPacket, null);
} catch (IOException e) {
System.out.println("Failed to write {} to switch {}: {}");
}
}
public void sendDHCPAck(IOFSwitch sw, short inPort, byte[] chaddr, int dstIPAddr,
int yiaddr, int 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 DHCPACKPacket = (OFPacketOut)
floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
DHCPACKPacket.setBufferId(-1);
Ethernet ethDHCPAck = new Ethernet();
ethDHCPAck.setSourceMACAddress(CONTROLLER_MAC);
ethDHCPAck.setDestinationMACAddress(chaddr);
ethDHCPAck.setEtherType(Ethernet.TYPE_IPv4);
IPv4 ipv4DHCPAck = new IPv4();
if (dstIPAddr == 0) {
ipv4DHCPAck.setDestinationAddress(BROADCAST_IP);
} else { // Client has IP and dhclient must have crashed
ipv4DHCPAck.setDestinationAddress(dstIPAddr);
}
ipv4DHCPAck.setSourceAddress(CONTROLLER_IP);
ipv4DHCPAck.setProtocol(IPv4.PROTOCOL_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);
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);
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);
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);
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)));
short packetOutLength = (short) OFPacketOut.MINIMUM_LENGTH;
DHCPACKPacket.setInPort(OFPort.OFPP_NONE.getValue());
DHCPACKPacket.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
packetOutLength = (short) (packetOutLength + OFActionOutput.MINIMUM_LENGTH);
List<OFAction> actions = new ArrayList<OFAction>(1);
actions.add(new OFActionOutput(inPort, (short) 0));
DHCPACKPacket.setActions(actions);
DHCPACKPacket.setPacketData(ethDHCPAck.serialize());
packetOutLength = (short) (packetOutLength + ethDHCPAck.serialize().length);
DHCPACKPacket.setLength(packetOutLength);
log.debug("Sending DHCP ACK");
try {
sw.write(DHCPACKPacket, null);
} catch (IOException e) {
System.out.println("Failed to write {} to switch {}: {}");
}
}
public void sendDHCPNack(IOFSwitch sw, short inPort, byte[] chaddr, int giaddr, int xid) {
OFPacketOut DHCPOfferPacket = (OFPacketOut)
floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
DHCPOfferPacket.setBufferId(-1);
Ethernet ethDHCPOffer = new Ethernet();
ethDHCPOffer.setSourceMACAddress(CONTROLLER_MAC);
ethDHCPOffer.setDestinationMACAddress(chaddr);
ethDHCPOffer.setEtherType(Ethernet.TYPE_IPv4);
IPv4 ipv4DHCPOffer = new IPv4();
ipv4DHCPOffer.setDestinationAddress(BROADCAST_IP);
ipv4DHCPOffer.setSourceAddress(CONTROLLER_IP);
ipv4DHCPOffer.setProtocol(IPv4.PROTOCOL_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);
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)));
short packetOutLength = (short) OFPacketOut.MINIMUM_LENGTH;
DHCPOfferPacket.setInPort(OFPort.OFPP_NONE.getValue());
DHCPOfferPacket.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
packetOutLength = (short) (packetOutLength + OFActionOutput.MINIMUM_LENGTH);
List<OFAction> actions = new ArrayList<OFAction>(1);
actions.add(new OFActionOutput(inPort, (short) 0));
DHCPOfferPacket.setActions(actions);
DHCPOfferPacket.setPacketData(ethDHCPOffer.serialize());
packetOutLength = (short) (packetOutLength + ethDHCPOffer.serialize().length);
DHCPOfferPacket.setLength(packetOutLength);
log.info("Sending DHCP NACK");
try {
sw.write(DHCPOfferPacket, null);
} catch (IOException e) {
System.out.println("Failed to write {} to switch {}: {}");
}
}
/**
* Creates an ARP request frame, puts it into a packet out message and
* sends the packet out message to all switch ports (attachment point ports)
* that are not connected to other OpenFlow switches.
*
*/
/*protected void sendARPReqest(ARPRequest arpRequest) {
// Create an ARP request frame
IPacket arpReply = new Ethernet()
.setSourceMACAddress(Ethernet.toByteArray(arpRequest.getSourceMACAddress()))
.setDestinationMACAddress(BROADCAST_MAC)
.setEtherType(Ethernet.TYPE_ARP)
.setPayload(new ARP()
.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setOpCode(ARP.OP_REQUEST)
.setHardwareAddressLength((byte)6)
.setProtocolAddressLength((byte)4)
.setSenderHardwareAddress(Ethernet.toByteArray(arpRequest.getSourceMACAddress()))
.setSenderProtocolAddress(IPv4.toIPv4AddressBytes((int)arpRequest.getSourceIPAddress()))
.setTargetHardwareAddress(Ethernet.toByteArray(arpRequest.getTargetMACAddress()))
.setTargetProtocolAddress(IPv4.toIPv4AddressBytes((int)arpRequest.getTargetIPAddress()))
.setPayload(new Data(new byte[] {0x01})));
// Send ARP request to all external ports (i.e. attachment point ports).
for (Map<long, IOFSwitch> switchId : floodlightProvider.getSwitches()) {
IOFSwitch sw = floodlightProvider.getSwitch(switchId);
for (OFPhysicalPort port : sw.getPorts()) {
short portId = port.getPortNumber();
if (switchId == arpRequest.getSwitchId() && portId == arpRequest.getInPort()) {
continue;
}
if (topologyManager.isAttachmentPointPort(switchId, portId))
this.sendPOMessage(arpReply, sw, portId);
if (log.isDebugEnabled()) {
log.debug("Send ARP request to " + HexString.toHexString(switchId) + " at port " + portId);
}
}
}
}*/
/**
* Creates an ARP reply frame, puts it into a packet out message and
* sends the packet out message to the switch that received the ARP
* request message.
*
*/
protected void sendARPReply(byte[] srcMac, byte[] dstMac, byte[] srcIP, byte[] dstIP, short inPort, IOFSwitch sw) {
// Create an ARP reply frame (from target (source) to source (destination)).
IPacket arpReply = new Ethernet()
.setSourceMACAddress(srcMac)
.setDestinationMACAddress(dstMac)
.setEtherType(Ethernet.TYPE_ARP)
.setPayload(new ARP()
.setHardwareType(ARP.HW_TYPE_ETHERNET)
.setProtocolType(ARP.PROTO_TYPE_IP)
.setOpCode(ARP.OP_REPLY)
.setHardwareAddressLength((byte) 6)
.setProtocolAddressLength((byte) 4)
.setSenderHardwareAddress(srcMac)
.setSenderProtocolAddress(srcIP)
.setTargetHardwareAddress(dstMac)
.setTargetProtocolAddress(dstIP)
.setPayload(new Data(new byte[] {0x01})));
// Send ARP reply.
sendPOMessage(arpReply, sw, inPort);
if (log.isDebugEnabled()) {
log.debug("Send ARP reply to " + HexString.toHexString(sw.getId()) + " at port " + inPort);
}
}
/**
* Creates and sends an OpenFlow PacketOut message containing the packet
* information to the switch. The packet included on the PacketOut message
* is sent out at the given port.
*
* @param packet The packet that is sent out.
* @param sw The switch the packet is sent out.
* @param port The port the packet is sent out.
*/
protected void sendPOMessage(IPacket packet, IOFSwitch sw, short port) {
// Serialize and wrap in a packet out
byte[] data = packet.serialize();
OFPacketOut po = (OFPacketOut) floodlightProvider.getOFMessageFactory().getMessage(OFType.PACKET_OUT);
po.setBufferId(OFPacketOut.BUFFER_ID_NONE);
po.setInPort(OFPort.OFPP_NONE);
// Set actions
List<OFAction> actions = new ArrayList<OFAction>();
actions.add(new OFActionOutput(port, (short) 0));
po.setActions(actions);
po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH);
// Set data
po.setLengthU(OFPacketOut.MINIMUM_LENGTH + po.getActionsLength() + data.length);
po.setPacketData(data);
// Send message
try {
sw.write(po, null);
} catch (IOException e) {
log.error("Failure sending ARP out port {} on switch {}", new Object[] { port, sw.getStringId() }, e);
}
}
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);
// Do Proxy ARP for all connected clients
if (eth.getEtherType() == Ethernet.TYPE_ARP) {
ARP ARPPayload = (ARP) eth.getPayload();
byte[] foundMAC;
if (ARPPayload.getOpCode() == ARP.OP_REQUEST) {
// First, check to see if the MAC being requested is the GW/router
if (Arrays.equals(DHCP_SERVER_ROUTER_IP, ARPPayload.getTargetProtocolAddress())) {
log.debug("Got ARP REQUEST for GW/Router. Sending Reply...");
sendARPReply(DHCP_SERVER_ROUTER_MAC, ARPPayload.getSenderHardwareAddress(),
ARPPayload.getTargetProtocolAddress(), ARPPayload.getSenderHardwareAddress(),
pi.getInPort(), sw);
// Then, check to see if it's a known and active client of the DHCP server
} else if ((foundMAC = theDHCPPool.lookupBoundMAC(ARPPayload.getSenderProtocolAddress())) != null) {
log.debug("Got ARP REQUEST for local IP. Sending Reply...");
sendARPReply(foundMAC, ARPPayload.getSenderHardwareAddress(),
ARPPayload.getTargetProtocolAddress(), ARPPayload.getSenderHardwareAddress(),
pi.getInPort(), sw);
// Otherwise, we need to let another machine handle the reply, so rebroadcast the request
} else {
// in the future, rebroadcast the ARP request
}
} else if (ARPPayload.getOpCode() == ARP.OP_REPLY) {
// do nothing for right now. After initial testing with the needed REQUEST handling, rebroadcast any REPLY
}
} else if (eth.getEtherType() == Ethernet.TYPE_IPv4) {
//log.debug("Got IPv4 Packet");
IPv4 IPv4Payload = (IPv4) eth.getPayload();
int IPv4SrcAddr = IPv4Payload.getSourceAddress();
if (IPv4Payload.getProtocol() == IPv4.PROTOCOL_UDP) {
//log.debug("Got UDP Packet");
UDP UDPPayload = (UDP) IPv4Payload.getPayload();
if ((UDPPayload.getDestinationPort() == UDP.DHCP_SERVER_PORT
|| UDPPayload.getDestinationPort() == UDP.DHCP_CLIENT_PORT)
&& (UDPPayload.getSourcePort() == UDP.DHCP_SERVER_PORT
|| UDPPayload.getSourcePort() == UDP.DHCP_CLIENT_PORT))
{
//log.debug("Got DHCP Packet");
// This is a DHCP packet that we need to process
DHCP DHCPPayload = (DHCP) UDPPayload.getPayload();
short inPort = pi.getInPort();
/* DHCP/IPv4 Header Information */
int xid = 0;
int yiaddr = 0;
int giaddr = 0;
byte[] chaddr = null;
byte[] 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 = Arrays.copyOf(DHCPPayload.getClientHardwareAddress(), DHCPPayload.getClientHardwareAddress().length);
List<DHCPOption> options = DHCPPayload.getOptions();
for (DHCPOption option : options) {
if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_IP) {
desiredIPAddr = Arrays.copyOf(option.getData(), option.getData().length);
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 " + MACAddress.valueOf(chaddr).toString() + " was not granted an IP lease");
return Command.CONTINUE;
}
DHCPBinding lease = theDHCPPool.getStaticLease(chaddr);
if (lease == null) {
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 = IPv4.toIPv4Address(lease.getIPv4AddressBytes());
log.debug("Got new lease for " + IPv4.fromIPv4Address(yiaddr) + " " + MACAddress.valueOf(chaddr).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 = IPv4.toIPv4Address(lease.getIPv4AddressBytes());
log.debug("Got new lease for " + IPv4.fromIPv4Address(yiaddr) + " " + MACAddress.valueOf(chaddr).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 = Arrays.copyOf(DHCPPayload.getClientHardwareAddress(), DHCPPayload.getClientHardwareAddress().length);
List<DHCPOption> options = DHCPPayload.getOptions();
for (DHCPOption option : options) {
if (option.getCode() == DHCP_REQ_PARAM_OPTION_CODE_REQUESTED_IP) {
desiredIPAddr = Arrays.copyOf(option.getData(), option.getData().length);
// TODO: Double-check to make sure checking if this is NULL breaks anything else somewhere (logic-wise)
if (theDHCPPool.getDHCPbindingFromMAC(chaddr) != null && !Arrays.equals(option.getData(), theDHCPPool.getDHCPbindingFromMAC(chaddr).getIPv4AddressBytes())) {
// 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 (!Arrays.equals(option.getData(), 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 " + MACAddress.valueOf(chaddr).toString() + " was not granted an IP lease");
return Command.CONTINUE;
}
DHCPBinding lease = theDHCPPool.getStaticLease(chaddr);
// Get any binding, in use now or not
if (desiredIPAddr != null && lease == null) {
lease = theDHCPPool.getDHCPbindingFromIPv4(desiredIPAddr);
} else if (lease == null) {
lease = theDHCPPool.getAnyAvailableLease(chaddr);
}
// This IP is not in our allocation range
if (lease == null) {
log.info("The IP " + IPv4.fromIPv4Address(IPv4.toIPv4Address(desiredIPAddr)) + " is not in the range "
+ IPv4.fromIPv4Address(IPv4.toIPv4Address(DHCP_SERVER_IP_START)) + " to " + IPv4.fromIPv4Address(IPv4.toIPv4Address(DHCP_SERVER_IP_STOP)));
log.info("Device with MAC " + MACAddress.valueOf(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.isMACMemberOf(chaddr) && lease.isActiveLease()) {
log.debug("Tried to REQUEST an IP that is currently assigned to another MAC");
log.debug("Device with MAC " + MACAddress.valueOf(chaddr).toString() + " was not granted an IP lease");
sendACK = false;
// Check if we want to renew the MAC's current lease
} else if (lease.isMACMemberOf(chaddr) && lease.isActiveLease()) {
log.debug("Renewing lease for MAC " + MACAddress.valueOf(chaddr).toString());
theDHCPPool.renewLease(lease.getIPv4AddressBytes(), DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS);
yiaddr = IPv4.toIPv4Address(lease.getIPv4AddressBytes());
log.debug("Finalized renewed lease for " + IPv4.fromIPv4Address(yiaddr) + " " + MACAddress.valueOf(chaddr).toString());
// Check if we want to create a new lease for the MAC
} else if (!lease.isActiveLease()){
log.debug("Assigning new lease for MAC " + MACAddress.valueOf(chaddr).toString());
theDHCPPool.setDHCPbinding(lease, chaddr, DHCP_SERVER_DEFAULT_LEASE_TIME_SECONDS);
yiaddr = IPv4.toIPv4Address(lease.getIPv4AddressBytes());
log.debug("Finalized renewed lease for " + IPv4.fromIPv4Address(yiaddr) + " " + MACAddress.valueOf(chaddr).toString());
} else {
log.debug("Don't know how we got here");
return Command.CONTINUE;
}
if (sendACK) {
updateClientLocation(lease, sw, inPort);
}
}
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 " + MACAddress.valueOf(DHCPPayload.getClientHardwareAddress()).toString());
log.info("IP " + theDHCPPool.getDHCPbindingFromMAC(DHCPPayload.getClientHardwareAddress()).getIPv4AddresString()
+ " is now available in the DHCP address pool");
} else {
log.debug("Lease of " + MACAddress.valueOf(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 " + MACAddress.valueOf(DHCPPayload.getClientHardwareAddress()).toString());
} else {
log.info("HOLD Lease of " + MACAddress.valueOf(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 = Arrays.copyOf(DHCPPayload.getClientHardwareAddress(), DHCPPayload.getClientHardwareAddress().length);
// 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) on switch " + sw.getStringId());
// can return Command.STOP here to prevent DHCP packets leaking out of the handoff network, but this would prevent replies
// from being passed through participating (but middle-man) OVS or physical OF switches.
} 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)
public void updateClientLocation(DHCPBinding clientBinding, IOFSwitch sw, short inPort) {
// If the client is new, then give it a home switch...
if (clientBinding.getHomeSwitch().equals("")) {
clientBinding.setHomeSwitch(sw.getStringId());
log.debug("Setting home switch for client " + clientBinding.getIPv4AddresString() + " " + clientBinding.getMACAddressesString()
+ " " + sw.getStringId());
// Set flows for the home (current) switch
if (sw.getStringId().equals(WIFI_NODE_WIFI_OVS_DPID)) {
log.info("Adding INITIAL WIFI FLOW for client " + clientBinding.getIPv4AddresString() + " at " + clientBinding.getHomeSwitch() );
OFFlowMod flow = new OFFlowMod();
OFMatch match = new OFMatch();
OFActionDataLayerDestination dldstAction = new OFActionDataLayerDestination();
ArrayList<OFAction> actionList = new ArrayList<OFAction>();
OFActionOutput outputAction = new OFActionOutput();
match.setInputPort(OFPort.OFPP_LOCAL.getValue());
match.setDataLayerType(Ethernet.TYPE_IPv4);
match.setNetworkDestination(IPv4.toIPv4Address(clientBinding.getIPv4AddressBytes())); // match only our client's packets
dldstAction.setType(OFActionType.SET_DL_DST);
dldstAction.setDataLayerAddress(clientBinding.getCurrentMACAddressBytes());
dldstAction.setLength((short) OFActionDataLayerDestination.MINIMUM_LENGTH);
actionList.add(dldstAction);
outputAction.setType(OFActionType.OUTPUT);
outputAction.setPort(ROOT_NODE_ROOT_OVS_WIFI_PATCH);
outputAction.setLength((short) OFActionOutput.MINIMUM_LENGTH);
actionList.add(outputAction);
flow.setCookie(0);
flow.setBufferId(-1);
flow.setOutPort(ROOT_NODE_ROOT_OVS_WIFI_PATCH);
flow.setActions(actionList);
flow.setMatch(match);
flow.setPriority((short) 32768);
flow.setLengthU(OFFlowMod.MINIMUM_LENGTH + outputAction.getLengthU() + dldstAction.getLengthU());
String flowName = "WiFi-client-" + clientBinding.getIPv4AddresString();
sfp.addFlow(flowName, flow, ROOT_NODE_ROOT_OVS_DPID);
clientBinding.setFlowName(flowName);
log.info("added flow on SW " + ROOT_NODE_ROOT_OVS_DPID + flowName);
actionList.clear();
} else if (sw.getStringId().equals(ROOT_NODE_WIMAX_OVS_DPID)) {
log.info("Adding INITIAL WIMAX FLOW for client " + clientBinding.getIPv4AddresString() + " at " + clientBinding.getHomeSwitch() );
OFFlowMod flow = new OFFlowMod();
OFMatch match = new OFMatch();
OFActionDataLayerDestination dldstAction = new OFActionDataLayerDestination();
ArrayList<OFAction> actionList = new ArrayList<OFAction>();
OFActionOutput outputAction = new OFActionOutput();
match.setInputPort(OFPort.OFPP_LOCAL.getValue());
match.setDataLayerType(Ethernet.TYPE_IPv4);
match.setNetworkDestination(IPv4.toIPv4Address(clientBinding.getIPv4AddressBytes())); // match only our client's packets
dldstAction.setType(OFActionType.SET_DL_DST);
dldstAction.setDataLayerAddress(clientBinding.getCurrentMACAddressBytes());
dldstAction.setLength((short) OFActionDataLayerDestination.MINIMUM_LENGTH);
actionList.add(dldstAction);
outputAction.setType(OFActionType.OUTPUT);
outputAction.setPort(ROOT_NODE_ROOT_OVS_WIMAX_PATCH);
outputAction.setLength((short) OFActionOutput.MINIMUM_LENGTH);
actionList.add(outputAction);
flow.setCookie(0);
flow.setBufferId(-1);
flow.setOutPort(ROOT_NODE_ROOT_OVS_WIMAX_PATCH);
flow.setActions(actionList);
flow.setMatch(match);
flow.setPriority((short) 32768);
flow.setLengthU(OFFlowMod.MINIMUM_LENGTH + outputAction.getLengthU() + dldstAction.getLengthU());
String flowName = "WiMAX-client-" + clientBinding.getIPv4AddresString();
sfp.addFlow(flowName, flow, ROOT_NODE_ROOT_OVS_DPID);
clientBinding.setFlowName(flowName);
log.info("added flow on SW " + ROOT_NODE_ROOT_OVS_DPID + flowName);
actionList.clear();
}
// If the client has already associated with a network (and thus has a flow name)
} else if (sw.getStringId().equals(ROOT_NODE_WIMAX_OVS_DPID)) {
if (clientBinding.getFlowName().startsWith("WiMAX")) {
log.debug("Client is still @ WiMAX!");
} else if (clientBinding.getFlowName().startsWith("WiFi")) {
// Client is new to the WiMAX network
sfp.deleteFlow(clientBinding.getFlowName());
clientBinding.setFlowName("");
log.debug("Client has arrived @ WiMAX!");
log.info("Adding WIMAX FLOW for client " + clientBinding.getIPv4AddresString() + " at " + clientBinding.getHomeSwitch() );
OFFlowMod flow = new OFFlowMod();
OFMatch match = new OFMatch();
OFActionDataLayerDestination dldstAction = new OFActionDataLayerDestination();
ArrayList<OFAction> actionList = new ArrayList<OFAction>();
OFActionOutput outputAction = new OFActionOutput();
match.setInputPort(OFPort.OFPP_LOCAL.getValue());
match.setDataLayerType(Ethernet.TYPE_IPv4);
match.setNetworkDestination(IPv4.toIPv4Address(clientBinding.getIPv4AddressBytes())); // match only our client's packets
dldstAction.setType(OFActionType.SET_DL_DST);
dldstAction.setDataLayerAddress(clientBinding.getCurrentMACAddressBytes());
dldstAction.setLength((short) OFActionDataLayerDestination.MINIMUM_LENGTH);
actionList.add(dldstAction);
outputAction.setType(OFActionType.OUTPUT);
outputAction.setPort(ROOT_NODE_ROOT_OVS_WIMAX_PATCH);
outputAction.setLength((short) OFActionOutput.MINIMUM_LENGTH);
actionList.add(outputAction);
flow.setCookie(0);
flow.setBufferId(-1);
flow.setOutPort(ROOT_NODE_ROOT_OVS_WIMAX_PATCH);
flow.setActions(actionList);
flow.setMatch(match);
flow.setPriority((short) 32768);
flow.setLengthU(OFFlowMod.MINIMUM_LENGTH + outputAction.getLengthU() + dldstAction.getLengthU());
String flowName = "WiMAX-client-" + clientBinding.getIPv4AddresString();
sfp.addFlow(flowName, flow, ROOT_NODE_ROOT_OVS_DPID);
clientBinding.setFlowName(flowName);
log.info("added flow on SW " + ROOT_NODE_ROOT_OVS_DPID + flowName);
actionList.clear();
}
} else if (sw.getStringId().equals(WIFI_NODE_WIFI_OVS_DPID)) {
if (clientBinding.getFlowName().startsWith("WiFi")) {
log.debug("Client is still @ WiFi!");
} else if (clientBinding.getFlowName().startsWith("WiMAX")) {
// Client is new to the WiFi network
sfp.deleteFlow(clientBinding.getFlowName());
clientBinding.setFlowName("");
log.debug("Client has arrived @ WiFi!");
log.info("Adding WIFI FLOW for client " + clientBinding.getIPv4AddresString() + " at " + clientBinding.getHomeSwitch() );
OFFlowMod flow = new OFFlowMod();
OFMatch match = new OFMatch();
OFActionDataLayerDestination dldstAction = new OFActionDataLayerDestination();
ArrayList<OFAction> actionList = new ArrayList<OFAction>();
OFActionOutput outputAction = new OFActionOutput();
match.setInputPort(OFPort.OFPP_LOCAL.getValue());
match.setDataLayerType(Ethernet.TYPE_IPv4);
match.setNetworkDestination(IPv4.toIPv4Address(clientBinding.getIPv4AddressBytes())); // match only our client's packets
dldstAction.setType(OFActionType.SET_DL_DST);
dldstAction.setDataLayerAddress(clientBinding.getCurrentMACAddressBytes());
dldstAction.setLength((short) OFActionDataLayerDestination.MINIMUM_LENGTH);
actionList.add(dldstAction);
outputAction.setType(OFActionType.OUTPUT);
outputAction.setPort(ROOT_NODE_ROOT_OVS_WIFI_PATCH);
outputAction.setLength((short) OFActionOutput.MINIMUM_LENGTH);
actionList.add(outputAction);
flow.setCookie(0);
flow.setBufferId(-1);
flow.setOutPort(ROOT_NODE_ROOT_OVS_WIFI_PATCH);
flow.setActions(actionList);
flow.setMatch(match);
flow.setPriority((short) 32768);
flow.setLengthU(OFFlowMod.MINIMUM_LENGTH + outputAction.getLengthU() + dldstAction.getLengthU());
String flowName = "WiFi-client-" + clientBinding.getIPv4AddresString();
sfp.addFlow(flowName, flow, ROOT_NODE_ROOT_OVS_DPID);
clientBinding.setFlowName(flowName);
log.info("added flow on SW " + ROOT_NODE_ROOT_OVS_DPID + flowName);
actionList.clear();
}
}
return;
}
/**
* 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.getMACAddressesString() + " has expired");
log.info("Lease now available for IP " + binding.getIPv4AddresString());
}
}
} // END DHCPLeasePolice Class
} // END DHCPServer Class