edu.uiuc.cs.net.DPRPManager
Class ServerPortMgr

java.lang.Object
  |
  +--edu.uiuc.cs.net.DPRPManager.ServerPortMgr

public class ServerPortMgr
extends java.lang.Object

The Server Port Manager - handles the management of leases and restrictions in the Server. This class has a lot of code because of it's dual nature -- it must track leases, as well as the server's security (client restrictions). When the server starts, it will instantiate a 'ServerPortManager', and then query it as requests are processed.

ABOUT THE LINUX PORT ALLOCATION CODE:
If an appropriate Linux-based machine with netfilter support is detected, the server will enable "linux mode" in the server port manager. In this mode, the allocFreeExternalPort() method will place a reservation rule for the client in the "destination NAT" netfilter chain. This will enable actual packets, sourced externally, to pass through the gateway (through the port specified) to the internal client host. This reservation is setup via the iptables command, which is prepared and exec'd by this code. The commands executed conform to the following template:

   /usr/sbin/iptables -v -t nat -A PREROUTING -i eth0 -j DNAT \
      -p <protocol> --destination <external IP> \
      --dport <allocPort> --to <clientIP>:<allocPort>
 
The following table details each parameter, and it's intended function:

Parameter: Description:
-v Enables verbosity, which is only useful for debugging.
-t nat Selects the "nat" table.
-A PREROUTING Selects the "PREROUTING" chain of the nat table. This means that the packets will be mangled before being passed to the IP routing code.
-i eth0 Specifies that the inbound interface for packets matching this rule should be eth0. NOTE: This should not be hard-coded, and will be parameterized in a future release.
-j DNAT Causes the packet match to jump to the "destination nat" builtin target.
-p <protocol> Sets the protocol (tcp or udp) type that packets must be in order to match.
--destination <external IP> Ensures that the packets are bound for the external address of the gateway before they are mangled.
--dport <allocPort> This is the actual port that we have reserved -- if the packet is bound for the external IP address, and matches this port, then it will be NAT'd and routed into our client.
--to <clientIP>:<allocPort> The ultimate destination for these packets -- an IP address and port on the client that made this reservation, and wants to eat these packets for lunch.

The final caveat to mention about this port allocation code is that I needed to devise some way in order to avoid conflicts between ports reserved by DPRP clients, and ports reserved in the gateway during the normal course of source NAT routing. Since the 'iptables' command doesn't emit an error in the case of such a conflict, I decided to make this an administrative issue. The range of ports allowable for use with SNAT needs to be restricted, and the left-overs given to the DPRP code.

ABOUT THE CLIENT RESTRICTION CODE:
The most code-intensive part of the client restrictions aspect is actually reading the restrictions in from disk. This involves a lot of text parsing, and the like, and is quite a pain. Nevertheless, here is the format for the restriction entries that the server can read in from disk:

   host 
        [renewAllowed = true | false]    default = true
        [numRetries = <#>]               default = 0 (unlimited)
        [portRange = <#> - <#>]          default = 0 - 65536
        [minDuration = <#>]              default = 500
        [maxDuration = <#>]              default = 2147483647
        [deny = true | false]            default = false

 At least one option must be specified for each host block. Comments can be 
 inserted anywhere, so long as they begin wtih a '#' character.
 

Version:
1.3 - October 30th, 2001
Author:
Andy Reitz (areitz@cs.uiuc.edu)

Field Summary
private  java.util.HashMap clientRestrictions
          Map that stores all of the client restrictions.
private  int curPort
          The current port to be allocated in the external range.
private static int DEFAULT_MAX_PORT
          The default maximum external port in the range.
private static int DEFAULT_MIN_PORT
          The default minimum external port in the range.
private  java.net.InetAddress externalIP
          The external IP address upon which we are allocating ports (lame).
private  boolean hostIsLinux
          Set to true if server is running on a compliant Linux/netfilter box.
private  java.lang.String leaseFileName
          The name of the on-disk lease database.
private  int maxExtPort
          The maximum external port of those which we are allowed to allocate.
private  int minExtPort
          The minimum allocatable external port, as configured.
private  java.util.HashMap portResv
          Map that stores all of the ports that have been reserved.
private  boolean[] resvdPorts
          A bit field, marking the ports in range that are reserved or not.
 
Constructor Summary
ServerPortMgr()
          Creates a new ServerPortManager instance.
 
Method Summary
 void addReservation(DPRPLease lea, boolean wf)
          Adds a lease to the Map, and optionally synchronizes the Map with disk.
 int allocExternalPort(java.net.InetAddress clientIP, int allocPort, byte prot)
          Reserves a specific external port in netfilter.
 int allocFreeExternalPort(java.net.InetAddress clientIP, byte prot)
           
 int allocLinuxPort(java.net.InetAddress clientIP, int allocPort, byte prot)
          Builds the iptables command that creates a DNAT hole on the gateway.
 boolean checkDenied(java.net.InetAddress cli)
          Check to see if a client is denied access to the DPRP service.
 boolean checkDuration(DPRPLease lea)
          Checks to see that the specified duration is in-range, with respect to client restrictions.
 boolean checkPort(DPRPLease lea)
          Checks to see if the external port in a given lease is okay.
 boolean checkRetries(DPRPLease lea)
          Examines the number of times a lease has been renewed.
 void delReservation(DPRPLease lea, boolean wf)
          Deletes a reservation (nee Lease) from the Map.
private  void execCmd(java.lang.String[] cmdAry)
          Actually makes the Runtime.exec() call, for the pre-setup command-array.
 int getMaxAllowedDuration(DPRPLease lea)
          Returns the maximum allowed duration for a given client.
 int getMaxExtPort()
          Returns the maximum external port in range.
 int getMinExtPort()
          Returns the minimum external port in range.
 DPRPLease getReservation(DPRPMessage inMessage)
          Retrieves a lease from the Map that corresponds to the given message.
 boolean isLinux()
          Returns true if Linux flag has been set, false otherwise.
 void markAsLinux()
          Sets the Linux flag.
private  DPRPRestriction parseAllHostOptions(java.net.InetAddress cliIP, java.lang.String curLine, java.io.LineNumberReader lnr)
          Parses out all of the options inside of a given host block.
private  boolean parseBoolean(java.util.StringTokenizer tok, int lineNum)
          Parses out a boolean value from the host security configuration file.
 int parseConfigFile(java.lang.String filename)
          Parses a "Host Security Configuration File" in order to build a map of 'DPRPRestriction' objects.
private  int parseInteger(java.util.StringTokenizer tok, java.lang.String sep, int lineNum)
          Parses out an integer value from the host security configuration file.
 void printConfigFormat()
          Prints the format of the configuration file to stderr.
 int readLeaseFile(java.lang.String fname)
          Sucks in the on-disk lease database, and reconstructs the lease map in memory.
 boolean renewOK(java.net.InetAddress cli)
          Checks to see if a specific client is allowed to negotiate for a lease renewal.
 void setExternalIP(java.net.InetAddress ia)
          Sets the external IP address.
 boolean setExtPortRange(int sm, int lm)
           
 boolean unallocExternalPort(java.net.InetAddress clientIP, int allocPort, byte prot)
          Removes a port allocation from the system.
private  boolean unallocLinuxPort(java.net.InetAddress clientIP, int allocPort, byte prot)
          Prepares the iptables command for unallocating a reservation.
 void updateReservation(DPRPLease lea)
          Updates the object stored at the primary key with a new lease object.
private  int writeLeaseFile()
          Writes all of the leases in the Map out to disk, and also purges dead leases along the way.
 
Methods inherited from class java.lang.Object
, clone, equals, finalize, getClass, hashCode, notify, notifyAll, registerNatives, toString, wait, wait, wait
 

Field Detail

portResv

private java.util.HashMap portResv
Map that stores all of the ports that have been reserved. It maps primary keys to DPRPLease objects. Also, it is important to remember that the HashMap is not thread-safe, and thus all accesses to it must be protected by a semaphore.

clientRestrictions

private java.util.HashMap clientRestrictions
Map that stores all of the client restrictions. This maps client InetAddress objects to DPRPRestriction objects. Also, it is important to remember that the HashMap is not thread-safe, and thus all accesses to it must be protected by a semaphore.

leaseFileName

private java.lang.String leaseFileName
The name of the on-disk lease database.

DEFAULT_MIN_PORT

private static final int DEFAULT_MIN_PORT
The default minimum external port in the range.

DEFAULT_MAX_PORT

private static final int DEFAULT_MAX_PORT
The default maximum external port in the range.

minExtPort

private int minExtPort
The minimum allocatable external port, as configured.

maxExtPort

private int maxExtPort
The maximum external port of those which we are allowed to allocate.

hostIsLinux

private boolean hostIsLinux
Set to true if server is running on a compliant Linux/netfilter box.

curPort

private int curPort
The current port to be allocated in the external range.

resvdPorts

private boolean[] resvdPorts
A bit field, marking the ports in range that are reserved or not.

externalIP

private java.net.InetAddress externalIP
The external IP address upon which we are allocating ports (lame).
Constructor Detail

ServerPortMgr

public ServerPortMgr()
Creates a new ServerPortManager instance.
Method Detail

checkPort

public boolean checkPort(DPRPLease lea)
Checks to see if the external port in a given lease is okay. By okay, I mean it checks the restrictions set in place for a particular client, and determines if the port is in the allowed range. This also checks to make sure that the specified protocol is kosher as well.
Parameters:
lea - A DPRPLease, whose external port is to be checked.
Returns:
True if the port is allowed, false otherwise.

checkDuration

public boolean checkDuration(DPRPLease lea)
Checks to see that the specified duration is in-range, with respect to client restrictions. This is done by extracting the duration in the given lease, and comparing it to the bounds specified in the restrictions for the client.
Parameters:
lea - A lease, specifying a client IP address and a duration.
Returns:
True if the duration satisfies the bounds, false otherwise.

renewOK

public boolean renewOK(java.net.InetAddress cli)
Checks to see if a specific client is allowed to negotiate for a lease renewal.
Parameters:
cli - A client IP address to be looked up.
Returns:
True if the client is allowed to renew (the default), false otherwise.

checkRetries

public boolean checkRetries(DPRPLease lea)
Examines the number of times a lease has been renewed. If the client is restricted to a certain number of renewals per lease, than this method will enforce this security measure.
Parameters:
lea - A lease, containing a client IP address and the number of times the lease has been renewed.
Returns:
True if the number of renews is less than rule, false otherwise.

checkDenied

public boolean checkDenied(java.net.InetAddress cli)
Check to see if a client is denied access to the DPRP service.
Parameters:
cli - A client IP address to be looked up in the restriction database.
Returns:
True if the client has been denied by rule, false otherwise.

getMaxAllowedDuration

public int getMaxAllowedDuration(DPRPLease lea)
Returns the maximum allowed duration for a given client. This is needed in order to determine if a server-suggested duration is in-bounds.
Parameters:
lea - A lease, containing a client IP address.
Returns:
The maximum allowed duration for that client, in seconds.

setExtPortRange

public boolean setExtPortRange(int sm,
                               int lm)

getMinExtPort

public int getMinExtPort()
Returns the minimum external port in range.

getMaxExtPort

public int getMaxExtPort()
Returns the maximum external port in range.

setExternalIP

public void setExternalIP(java.net.InetAddress ia)
Sets the external IP address.

markAsLinux

public void markAsLinux()
Sets the Linux flag.

isLinux

public boolean isLinux()
Returns true if Linux flag has been set, false otherwise.

allocFreeExternalPort

public int allocFreeExternalPort(java.net.InetAddress clientIP,
                                 byte prot)
                          throws DPRPAllocatePortException

allocExternalPort

public int allocExternalPort(java.net.InetAddress clientIP,
                             int allocPort,
                             byte prot)
                      throws DPRPAllocatePortException
Reserves a specific external port in netfilter. This should also really be a private method.
Parameters:
clientIP - The clients IP address for this reservation.
allocPort - The external port to be reserved for the client.
prot - The IP protocol for this reservation.
Returns:
The port number that was allocated.
Throws:
DPRPAllocatePortException - If an error occurs down in allocLinuxPort, or if clientIP is null.

allocLinuxPort

public int allocLinuxPort(java.net.InetAddress clientIP,
                          int allocPort,
                          byte prot)
                   throws DPRPAllocatePortException
Builds the iptables command that creates a DNAT hole on the gateway. I really wish that this method was marked as private. Also, I need to configure the internal and external interface names into the SPM.
Parameters:
clientIP - The clients IP address for this reservation.
allocPort - The external port to be reserved for the client.
prot - The IP protocol for this reservation.
Returns:
The port number that was allocated.
Throws:
DPRPAllocatePortException - If an error occurs down in execCmd.

unallocExternalPort

public boolean unallocExternalPort(java.net.InetAddress clientIP,
                                   int allocPort,
                                   byte prot)
Removes a port allocation from the system. This entails marking the appropriate location in the resvdPorts array as false, and then exec'ing the proper iptables command, via the unallocLinuxPort method.
Parameters:
clientIP - The clients IP address for this reservation.
allocPort - The external port reserved for the client.
prot - The IP protocol for this reservation.
Returns:
True if the exec() goes okay, false otherwise.
See Also:
(InetAddress clientIP, int allocPort, byte prot)

unallocLinuxPort

private boolean unallocLinuxPort(java.net.InetAddress clientIP,
                                 int allocPort,
                                 byte prot)
Prepares the iptables command for unallocating a reservation. I had a hard time realizing this method, because it seemed like deleting a rule had to be done by rule number. Tracking rule numbers would essentially de-volve into running a list command, parsing, and then finding the right number. But even that could fail, if a spry administrator were to change the rule numbers in-between the list and the delete. Luckily, I noticed that rule delete's can occur by specifying the complete rule, which makes things dramatically easier!
Parameters:
clientIP - The clients IP address for this reservation.
allocPort - The external port reserved for the client.
prot - The IP protocol for this reservation.
Returns:
True if the exec() goes okay, false otherwise.

execCmd

private void execCmd(java.lang.String[] cmdAry)
              throws DPRPAllocatePortException
Actually makes the Runtime.exec() call, for the pre-setup command-array. The command array format is such that the command itself (with full path name, just to be safe) is in slot 0. Each parameter (that would normally be space-separated if invoking from a shell) are in the ajoining array slots. If no exception is thrown, then it is presumed that the command completed successfully.
Parameters:
cmdAry - A properly-formatted command array, for the command to be executed.
Throws:
DPRPAllocatePortException - If something goes arwy.

addReservation

public void addReservation(DPRPLease lea,
                           boolean wf)
Adds a lease to the Map, and optionally synchronizes the Map with disk.
Parameters:
lea - The lease object to be added.
wf - If true, writes the leases out to disk.

delReservation

public void delReservation(DPRPLease lea,
                           boolean wf)
Deletes a reservation (nee Lease) from the Map.
Parameters:
lea - The lease object to be deleted.
wf - Controls wether-or-not the leases should be flushed to disk. As an optimization in some cases, the writing of the leases to disk can be handled by the callee.

updateReservation

public void updateReservation(DPRPLease lea)
Updates the object stored at the primary key with a new lease object.
Parameters:
lea - The new lease object that replaces a prior object.

getReservation

public DPRPLease getReservation(DPRPMessage inMessage)
Retrieves a lease from the Map that corresponds to the given message.
Parameters:
inMessage - A valid DPRPMessage, which will be used to locate a lease.
Returns:
A valid DPRPLease, if found.

writeLeaseFile

private int writeLeaseFile()
Writes all of the leases in the Map out to disk, and also purges dead leases along the way.
Returns:
The number of leases written.

readLeaseFile

public int readLeaseFile(java.lang.String fname)
                  throws java.io.IOException
Sucks in the on-disk lease database, and reconstructs the lease map in memory. It is called once during server startup, and if the specified file doesn't exist, then no harm is done, and the server starts with a clean lease database. However, if the lease file does exist, then this method reads in each lease string, creates a DPRPLease object, and inserts each object into the portResv map. Furthermore, each lease is checked to make sure that it hasn't expired. If it has, then it is not inserted into the database.
Parameters:
fname - The name of the file to open.
Returns:
The number of leases created.
Throws:
java.io.IOException - If an error occurs dealing with the file (usually, indicates that the given file doesn't exist).

parseConfigFile

public int parseConfigFile(java.lang.String filename)
                    throws DPRPConfigException,
                           java.io.IOException
Parses a "Host Security Configuration File" in order to build a map of 'DPRPRestriction' objects.
Parameters:
filename - The name of the file to be parsed.
Returns:
The number of host blocks successfully parsed.
Throws:
DPRPConfigException - If some sort of parsing error is encountered within the configuration file.
java.io.IOException - If an IO error occurs.

parseAllHostOptions

private DPRPRestriction parseAllHostOptions(java.net.InetAddress cliIP,
                                            java.lang.String curLine,
                                            java.io.LineNumberReader lnr)
                                     throws DPRPConfigException,
                                            java.io.IOException
Parses out all of the options inside of a given host block. This means that we loop, grabbing successive lines of text, until a new "host" block is encountered, or EOF. For each line, we check to see what option it specifies, and then farm off the parameters to individual parsing methods. Ain't I a stinkah?
Parameters:
cliIP - The client IP address that we're currently parsing options for.
curLine - The current line in the configuration file.
lnr - The buffer that we get new lines from.
Returns:
A fully-formed DPRPRestriction object.
Throws:
DPRPConfigException - If one of the options was invalid.
java.io.IOException - If there was a problem reading from the file.

parseInteger

private int parseInteger(java.util.StringTokenizer tok,
                         java.lang.String sep,
                         int lineNum)
                  throws DPRPConfigException
Parses out an integer value from the host security configuration file. This is accomplished by looking for the separator character from the string tokenizer, and then a valid integer number.
Parameters:
tok - A string tokenizer, from which we can extract tokens.
sep - A string representing the separator, what we should look for first. This gives this method a degree of flexability.
lineNum - The current line number that we're on, used to make pretty error messages.
Returns:
The int representation of the number found.
Throws:
DPRPConfigException - If we don't find valid input.

parseBoolean

private boolean parseBoolean(java.util.StringTokenizer tok,
                             int lineNum)
                      throws DPRPConfigException
Parses out a boolean value from the host security configuration file. This is accomplished by looking for an "=" character from the string tokenizer, and then either the "true" or "false" tokens.
Parameters:
tok - A string tokenizer, from which we can extract tokens.
lineNum - The current line number that we're on, used to make pretty error messages.
Returns:
The boolean representation of the string value found.
Throws:
DPRPConfigException - If we don't find valid input.

printConfigFormat

public void printConfigFormat()
Prints the format of the configuration file to stderr.