001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *  
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *  
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License. 
018     *  
019     */
020    package org.apache.directory.server.dhcp.store;
021    
022    
023    import java.net.InetAddress;
024    import java.util.Map;
025    
026    import org.apache.directory.server.dhcp.DhcpException;
027    import org.apache.directory.server.dhcp.messages.HardwareAddress;
028    import org.apache.directory.server.dhcp.options.OptionsField;
029    import org.apache.directory.server.dhcp.options.vendor.HostName;
030    import org.apache.directory.server.dhcp.options.vendor.SubnetMask;
031    import org.apache.directory.server.dhcp.service.Lease;
032    import org.slf4j.Logger;
033    import org.slf4j.LoggerFactory;
034    
035    
036    /**
037     * Abstract base implementation of a {@link DhcpStore}.
038     * 
039     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
040     * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $
041     */
042    public abstract class AbstractDhcpStore implements DhcpStore
043    {
044        private static final Logger logger = LoggerFactory.getLogger( AbstractDhcpStore.class );
045    
046    
047        /*
048         * @see org.apache.directory.server.dhcp.service.DhcpStore#getLeaseOffer(org.apache.directory.server.dhcp.messages.HardwareAddress,
049         *      java.net.InetAddress, java.net.InetAddress, long,
050         *      org.apache.directory.server.dhcp.options.OptionsField)
051         */
052        public Lease getLeaseOffer( HardwareAddress hardwareAddress, InetAddress requestedAddress,
053            InetAddress selectionBase, long requestedLeaseTime, OptionsField options ) throws DhcpException
054        {
055            Subnet subnet = findSubnet( selectionBase );
056            if ( null == subnet )
057            {
058                logger.warn( "Don't know anything about the sbnet containing " + selectionBase );
059                return null;
060            }
061    
062            // try to find existing lease
063            Lease lease = null;
064            lease = findExistingLease( hardwareAddress, lease );
065            if ( null != lease )
066                return lease;
067    
068            Host host = null;
069            host = findDesignatedHost( hardwareAddress );
070            if ( null != host )
071            {
072                // make sure that the host is actually within the subnet. Depending
073                // on the way the DhcpStore configuration is implemented, it is not
074                // possible to violate this condition, but we can't be sure.
075                if ( !subnet.contains( host.getAddress() ) )
076                {
077                    logger.warn( "Host " + host + " is not within the subnet for which an address is requested" );
078                }
079                else
080                {
081                    // build properties map
082                    Map properties = getProperties( subnet );
083                    properties.putAll( getProperties( host ) );
084    
085                    // build lease
086                    lease = new Lease();
087                    lease.setAcquired( System.currentTimeMillis() );
088    
089                    long leaseTime = determineLeaseTime( requestedLeaseTime, properties );
090    
091                    lease.setExpires( System.currentTimeMillis() + leaseTime );
092    
093                    lease.setHardwareAddress( hardwareAddress );
094                    lease.setState( Lease.STATE_NEW );
095                    lease.setClientAddress( host.getAddress() );
096    
097                    // set lease options
098                    OptionsField o = lease.getOptions();
099    
100                    // set (client) host name
101                    o.add( new HostName( host.getName() ) );
102    
103                    // add subnet settings
104                    o.add( new SubnetMask( subnet.getNetmask() ) );
105                    o.merge( subnet.getOptions() );
106    
107                    // add the host's options. they override existing
108                    // subnet options as they take the precedence.
109                    o.merge( host.getOptions() );
110                }
111            }
112    
113            if ( null == lease )
114            {
115                // FIXME: use selection base to find a lease in a pool.
116            }
117    
118            // update the lease state
119            if ( null != lease && lease.getState() != Lease.STATE_ACTIVE )
120            {
121                lease.setState( Lease.STATE_OFFERED );
122                updateLease( lease );
123            }
124    
125            return lease;
126        }
127    
128    
129        /*
130         * @see org.apache.directory.server.dhcp.store.DhcpStore#getExistingLease(org.apache.directory.server.dhcp.messages.HardwareAddress,
131         *      java.net.InetAddress, java.net.InetAddress, long,
132         *      org.apache.directory.server.dhcp.options.OptionsField)
133         */
134        public Lease getExistingLease( HardwareAddress hardwareAddress, InetAddress requestedAddress,
135            InetAddress selectionBase, long requestedLeaseTime, OptionsField options ) throws DhcpException
136        {
137            // try to find existing lease. if we don't find a lease based on the
138            // client's
139            // hardware address, we send a NAK.
140            Lease lease = null;
141            lease = findExistingLease( hardwareAddress, lease );
142            if ( null == lease )
143                return null;
144    
145            // check whether the notions of the client address match
146            if ( !lease.getClientAddress().equals( requestedAddress ) )
147            {
148                logger.warn( "Requested address " + requestedAddress + " for " + hardwareAddress
149                    + " doesn't match existing lease " + lease );
150                return null;
151            }
152    
153            // check whether addresses and subnet match
154            Subnet subnet = findSubnet( selectionBase );
155            if ( null == subnet )
156            {
157                logger.warn( "No subnet found for existing lease " + lease );
158                return null;
159            }
160            if ( !subnet.contains( lease.getClientAddress() ) )
161            {
162                logger.warn( "Client with existing lease " + lease + " is on wrong subnet " + subnet );
163                return null;
164            }
165            if ( !subnet.isInRange( lease.getClientAddress() ) )
166            {
167                logger.warn( "Client with existing lease " + lease + " is out of valid range for subnet " + subnet );
168                return null;
169            }
170    
171            // build properties map
172            Map properties = getProperties( subnet );
173    
174            // update lease options
175            OptionsField o = lease.getOptions();
176            o.clear();
177    
178            // add subnet settings
179            o.add( new SubnetMask( subnet.getNetmask() ) );
180            o.merge( subnet.getOptions() );
181    
182            // check whether there is a designated host.
183            Host host = findDesignatedHost( hardwareAddress );
184            if ( null != host )
185            {
186                // check whether the host matches the address (using a fixed
187                // host address is mandatory).
188                if ( host.getAddress() != null && !host.getAddress().equals( lease.getClientAddress() ) )
189                {
190                    logger.warn( "Existing fixed address for " + hardwareAddress + " conflicts with existing lease "
191                        + lease );
192                    return null;
193                }
194    
195                properties.putAll( getProperties( host ) );
196    
197                // set (client) host name
198                o.add( new HostName( host.getName() ) );
199    
200                // add the host's options
201                o.merge( host.getOptions() );
202            }
203    
204            // update other lease fields
205            long leaseTime = determineLeaseTime( requestedLeaseTime, properties );
206            lease.setExpires( System.currentTimeMillis() + leaseTime );
207            lease.setHardwareAddress( hardwareAddress );
208    
209            // update the lease state
210            if ( lease.getState() != Lease.STATE_ACTIVE )
211            {
212                lease.setState( Lease.STATE_ACTIVE );
213                updateLease( lease );
214            }
215    
216            // store information about the lease
217            updateLease( lease );
218    
219            return lease;
220        }
221    
222    
223        /**
224         * Determine the lease time based on the time requested by the client, the
225         * properties and a global default.
226         * 
227         * @param requestedLeaseTime
228         * @param properties
229         * @return long
230         */
231        private long determineLeaseTime( long requestedLeaseTime, Map properties )
232        {
233            // built-in default
234            long leaseTime = 1000L * 3600;
235            Integer propMaxLeaseTime = ( Integer ) properties.get( DhcpConfigElement.PROPERTY_MAX_LEASE_TIME );
236            if ( null != propMaxLeaseTime )
237                if ( requestedLeaseTime > 0 )
238                    leaseTime = Math.min( propMaxLeaseTime.intValue() * 1000L, requestedLeaseTime );
239                else
240                    leaseTime = propMaxLeaseTime.intValue() * 1000L;
241            return leaseTime;
242        }
243    
244    
245        /*
246         * @see org.apache.directory.server.dhcp.store.DhcpStore#releaseLease(org.apache.directory.server.dhcp.service.Lease)
247         */
248        public void releaseLease( Lease lease )
249        {
250            lease.setState( Lease.STATE_RELEASED );
251            updateLease( lease );
252        }
253    
254    
255        /**
256         * Update the (possibly changed) lease in the store.
257         * 
258         * @param lease
259         */
260        protected abstract void updateLease( Lease lease );
261    
262    
263        /**
264         * Return a list of all options applicable to the given config element. List
265         * list must contain the options specified for the element and all parent
266         * elements in an aggregated fashion. For instance, the options for a host
267         * must include the global default options, the options of classes the host
268         * is a member of, the host's group options and the host's options.
269         * 
270         * @param element
271         * @return OptionsField
272         */
273        protected abstract OptionsField getOptions( DhcpConfigElement element );
274    
275    
276        /**
277         * Return a list of all options applicable to the given config element. List
278         * list must contain the options specified for the element and all parent
279         * elements in an aggregated fashion. For instance, the options for a host
280         * must include the global default options, the options of classes the host
281         * is a member of, the host's group options and the host's options.
282         * 
283         * @param element
284         * @return Map
285         */
286        protected abstract Map getProperties( DhcpConfigElement element );
287    
288    
289        /**
290         * Find an existing lease in the store.
291         * 
292         * @param hardwareAddress
293         * @param existingLease
294         * @return Map
295         */
296        protected abstract Lease findExistingLease( HardwareAddress hardwareAddress, Lease existingLease );
297    
298    
299        /**
300         * Find a host to with the explicitely designated hardware address.
301         * 
302         * @param hardwareAddress
303         * @return Host
304         * @throws DhcpException
305         */
306        protected abstract Host findDesignatedHost( HardwareAddress hardwareAddress ) throws DhcpException;
307    
308    
309        /**
310         * Find the subnet definition matching the given address.
311         * 
312         * @param clientAddress
313         * @return Subnet
314         */
315        protected abstract Subnet findSubnet( InetAddress clientAddress );
316    }