blob: 8dfbda6936cbe697f556245fb5202462a3452913 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.server.dhcp.service;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import org.apache.directory.server.dhcp.DhcpException;
import org.apache.directory.server.dhcp.messages.DhcpMessage;
import org.apache.directory.server.dhcp.messages.MessageType;
import org.apache.directory.server.dhcp.options.AddressOption;
import org.apache.directory.server.dhcp.options.OptionsField;
import org.apache.directory.server.dhcp.options.dhcp.ClientIdentifier;
import org.apache.directory.server.dhcp.options.dhcp.IpAddressLeaseTime;
import org.apache.directory.server.dhcp.options.dhcp.MaximumDhcpMessageSize;
import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;
import org.apache.directory.server.dhcp.options.dhcp.RequestedIpAddress;
import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;
import org.apache.directory.server.dhcp.store.DhcpStore;
/**
* A default implementation of the DHCP service. Does the tedious low-level
* chores of handling DHCP messages, but delegates the lease-handling to a
* supplied DhcpStore.
*
* @see org.apache.directory.server.dhcp.store.DhcpStore
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $
*/
public class StoreBasedDhcpService extends AbstractDhcpService
{
private final DhcpStore dhcpStore;
public StoreBasedDhcpService(DhcpStore dhcpStore)
{
this.dhcpStore = dhcpStore;
}
/**
* Try to get an existing lease. The lease may have been created during
* earlier DHCP negotiations or a recent DHCPDISCOVER.
*
* @param clientAddress
* @param request
* @return
* @throws DhcpException
*/
private Lease getExistingLease( InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException
{
// determine requested lease time
IpAddressLeaseTime requestedLeaseTimeOption = ( IpAddressLeaseTime ) request.getOptions().get(
IpAddressLeaseTime.class );
long requestedLeaseTime = null != requestedLeaseTimeOption ? requestedLeaseTimeOption.getIntValue() * 1000
: -1L;
// try to get the lease (address) requested by the client
InetAddress requestedAddress = null;
AddressOption requestedAddressOption = ( AddressOption ) request.getOptions().get( RequestedIpAddress.class );
if ( null != requestedAddressOption )
requestedAddress = requestedAddressOption.getAddress();
if ( null == requestedAddress )
requestedAddress = request.getCurrentClientAddress();
InetAddress selectionBase = determineSelectionBase( clientAddress, request );
Lease lease = dhcpStore.getExistingLease( request.getHardwareAddress(), requestedAddress, selectionBase,
requestedLeaseTime, request.getOptions() );
if ( null == lease )
return null;
return lease;
}
/**
* Determine a lease to offer in response to a DHCPDISCOVER message.
* <p>
* When a server receives a DHCPDISCOVER message from a client, the server
* chooses a network address for the requesting client. If no address is
* available, the server may choose to report the problem to the system
* administrator. If an address is available, the new address SHOULD be
* chosen as follows:
* <ul>
* <li> The client's current address as recorded in the client's current
* binding, ELSE
* <li> The client's previous address as recorded in the client's (now
* expired or released) binding, if that address is in the server's pool of
* available addresses and not already allocated, ELSE
* <li> The address requested in the 'Requested IP Address' option, if that
* address is valid and not already allocated, ELSE
* <li> A new address allocated from the server's pool of available
* addresses; the address is selected based on the subnet from which the
* message was received (if 'giaddr' is 0) or on the address of the relay
* agent that forwarded the message ('giaddr' when not 0).
* </ul>
*
* @param clientAddress
* @param request
* @return
*/
private Lease getLeaseOffer( InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException
{
// determine requested lease time
IpAddressLeaseTime requestedLeaseTimeOption = ( IpAddressLeaseTime ) request.getOptions().get(
IpAddressLeaseTime.class );
long requestedLeaseTime = null != requestedLeaseTimeOption ? requestedLeaseTimeOption.getIntValue() * 1000
: -1L;
// try to get the lease (address) requested by the client
InetAddress requestedAddress = null;
AddressOption requestedAddressOption = ( AddressOption ) request.getOptions().get( RequestedIpAddress.class );
if ( null != requestedAddressOption )
requestedAddress = requestedAddressOption.getAddress();
InetAddress selectionBase = determineSelectionBase( clientAddress, request );
Lease lease = dhcpStore.getLeaseOffer( request.getHardwareAddress(), requestedAddress, selectionBase,
requestedLeaseTime, request.getOptions() );
return lease;
}
/*
* @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleRELEASE(java.net.InetSocketAddress,
* java.net.InetSocketAddress,
* org.apache.directory.server.dhcp.messages.DhcpMessage)
*/
protected DhcpMessage handleRELEASE( InetSocketAddress localAddress, InetSocketAddress clientAddress,
DhcpMessage request ) throws DhcpException
{
// check server ident
AddressOption serverIdentOption = ( AddressOption ) request.getOptions().get( ServerIdentifier.class );
if ( null != serverIdentOption && serverIdentOption.getAddress().isAnyLocalAddress() )
return null; // not me?! FIXME: handle authoritative server case
Lease lease = getExistingLease( clientAddress, request );
DhcpMessage reply = initGeneralReply( localAddress, request );
if ( null == lease )
{
// null lease? send NAK
// FIXME...
reply.setMessageType( MessageType.DHCPNAK );
reply.setCurrentClientAddress( null );
reply.setAssignedClientAddress( null );
reply.setNextServerAddress( null );
}
else
{
dhcpStore.releaseLease( lease );
// lease Ok, send ACK
// FIXME...
reply.getOptions().merge( lease.getOptions() );
reply.setAssignedClientAddress( lease.getClientAddress() );
reply.setNextServerAddress( lease.getNextServerAddress() );
// fix options
OptionsField options = reply.getOptions();
// these options must not be present
options.remove( RequestedIpAddress.class );
options.remove( ParameterRequestList.class );
options.remove( ClientIdentifier.class );
options.remove( MaximumDhcpMessageSize.class );
// these options must be present
options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );
stripUnwantedOptions( request, options );
}
return reply;
}
/*
* @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleDISCOVER(java.net.InetSocketAddress,
* org.apache.directory.server.dhcp.messages.DhcpMessage)
*/
protected DhcpMessage handleDISCOVER( InetSocketAddress localAddress, InetSocketAddress clientAddress,
DhcpMessage request ) throws DhcpException
{
Lease lease = getLeaseOffer( clientAddress, request );
// null lease? don't offer one.
if ( null == lease )
return null;
DhcpMessage reply = initGeneralReply( localAddress, request );
reply.getOptions().merge( lease.getOptions() );
reply.setMessageType( MessageType.DHCPOFFER );
reply.setAssignedClientAddress( lease.getClientAddress() );
reply.setNextServerAddress( lease.getNextServerAddress() );
// fix options
OptionsField options = reply.getOptions();
// these options must not be present
options.remove( RequestedIpAddress.class );
options.remove( ParameterRequestList.class );
options.remove( ClientIdentifier.class );
options.remove( MaximumDhcpMessageSize.class );
// these options must be present
options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );
stripUnwantedOptions( request, options );
return reply;
}
/*
* @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleREQUEST(java.net.InetSocketAddress,
* org.apache.directory.server.dhcp.messages.DhcpMessage)
*/
protected DhcpMessage handleREQUEST( InetSocketAddress localAddress, InetSocketAddress clientAddress,
DhcpMessage request ) throws DhcpException
{
// check server ident
AddressOption serverIdentOption = ( AddressOption ) request.getOptions().get( ServerIdentifier.class );
if ( null != serverIdentOption && serverIdentOption.getAddress().isAnyLocalAddress() )
return null; // not me?! FIXME: handle authoritative server case
Lease lease = getExistingLease( clientAddress, request );
DhcpMessage reply = initGeneralReply( localAddress, request );
if ( null == lease )
{
// null lease? send NAK
reply.setMessageType( MessageType.DHCPNAK );
reply.setCurrentClientAddress( null );
reply.setAssignedClientAddress( null );
reply.setNextServerAddress( null );
}
else
{
// lease Ok, send ACK
reply.getOptions().merge( lease.getOptions() );
reply.setAssignedClientAddress( lease.getClientAddress() );
reply.setNextServerAddress( lease.getNextServerAddress() );
// fix options
OptionsField options = reply.getOptions();
// these options must not be present
options.remove( RequestedIpAddress.class );
options.remove( ParameterRequestList.class );
options.remove( ClientIdentifier.class );
options.remove( MaximumDhcpMessageSize.class );
// these options must be present
options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );
stripUnwantedOptions( request, options );
}
return reply;
}
}