View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.dhcp.store;
21  
22  
23  import java.net.InetAddress;
24  import java.util.Map;
25  
26  import org.apache.directory.server.dhcp.DhcpException;
27  import org.apache.directory.server.dhcp.messages.HardwareAddress;
28  import org.apache.directory.server.dhcp.options.OptionsField;
29  import org.apache.directory.server.dhcp.options.vendor.HostName;
30  import org.apache.directory.server.dhcp.options.vendor.SubnetMask;
31  import org.apache.directory.server.dhcp.service.Lease;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  
36  /**
37   * Abstract base implementation of a {@link DhcpStore}.
38   * 
39   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
40   */
41  public abstract class AbstractDhcpStore implements DhcpStore
42  {
43      private static final Logger LOG = LoggerFactory.getLogger( AbstractDhcpStore.class );
44  
45  
46      /*
47       * @see org.apache.directory.server.dhcp.service.DhcpStore#getLeaseOffer(org.apache.directory.server.dhcp.messages.HardwareAddress,
48       *      java.net.InetAddress, java.net.InetAddress, long,
49       *      org.apache.directory.server.dhcp.options.OptionsField)
50       */
51      public Lease getLeaseOffer( HardwareAddress hardwareAddress, InetAddress requestedAddress,
52          InetAddress selectionBase, long requestedLeaseTime, OptionsField options ) throws DhcpException
53      {
54          Subnet subnet = findSubnet( selectionBase );
55  
56          if ( null == subnet )
57          {
58              LOG.warn( "Don't know anything about the sbnet containing {}", selectionBase );
59              return null;
60          }
61  
62          // try to find existing lease
63          Lease lease = null;
64          lease = findExistingLease( hardwareAddress, lease );
65  
66          if ( null != lease )
67          {
68              return lease;
69          }
70  
71          Host host = null;
72          host = findDesignatedHost( hardwareAddress );
73  
74          if ( null != host )
75          {
76              // make sure that the host is actually within the subnet. Depending
77              // on the way the DhcpStore configuration is implemented, it is not
78              // possible to violate this condition, but we can't be sure.
79              if ( !subnet.contains( host.getAddress() ) )
80              {
81                  LOG.warn( "Host {} is not within the subnet for which an address is requested", host );
82              }
83              else
84              {
85                  // build properties map
86                  Map properties = getProperties( subnet );
87                  properties.putAll( getProperties( host ) );
88  
89                  // build lease
90                  lease = new Lease();
91                  lease.setAcquired( System.currentTimeMillis() );
92  
93                  long leaseTime = determineLeaseTime( requestedLeaseTime, properties );
94  
95                  lease.setExpires( System.currentTimeMillis() + leaseTime );
96  
97                  lease.setHardwareAddress( hardwareAddress );
98                  lease.setState( Lease.STATE_NEW );
99                  lease.setClientAddress( host.getAddress() );
100 
101                 // set lease options
102                 OptionsField o = lease.getOptions();
103 
104                 // set (client) host name
105                 o.add( new HostName( host.getName() ) );
106 
107                 // add subnet settings
108                 o.add( new SubnetMask( subnet.getNetmask() ) );
109                 o.merge( subnet.getOptions() );
110 
111                 // add the host's options. they override existing
112                 // subnet options as they take the precedence.
113                 o.merge( host.getOptions() );
114             }
115         }
116 
117         // update the lease state
118         if ( null != lease && lease.getState() != Lease.STATE_ACTIVE )
119         {
120             lease.setState( Lease.STATE_OFFERED );
121             updateLease( lease );
122         }
123 
124         return lease;
125     }
126 
127 
128     /*
129      * @see org.apache.directory.server.dhcp.store.DhcpStore#getExistingLease(org.apache.directory.server.dhcp.messages.HardwareAddress,
130      *      java.net.InetAddress, java.net.InetAddress, long,
131      *      org.apache.directory.server.dhcp.options.OptionsField)
132      */
133     public Lease getExistingLease( HardwareAddress hardwareAddress, InetAddress requestedAddress,
134         InetAddress selectionBase, long requestedLeaseTime, OptionsField options ) throws DhcpException
135     {
136         // try to find existing lease. if we don't find a lease based on the
137         // client's
138         // hardware address, we send a NAK.
139         Lease lease = null;
140         lease = findExistingLease( hardwareAddress, lease );
141 
142         if ( null == lease )
143         {
144             return null;
145         }
146 
147         // check whether the notions of the client address match
148         if ( !lease.getClientAddress().equals( requestedAddress ) )
149         {
150             LOG.warn( "Requested address " + requestedAddress + " for " + hardwareAddress
151                 + " doesn't match existing lease " + lease );
152             return null;
153         }
154 
155         // check whether addresses and subnet match
156         Subnet subnet = findSubnet( selectionBase );
157 
158         if ( null == subnet )
159         {
160             LOG.warn( "No subnet found for existing lease {}", lease );
161             return null;
162         }
163 
164         if ( !subnet.contains( lease.getClientAddress() ) )
165         {
166             LOG.warn( "Client with existing lease {} is on wrong subnet {}", lease, subnet );
167             return null;
168         }
169 
170         if ( !subnet.isInRange( lease.getClientAddress() ) )
171         {
172             LOG.warn( "Client with existing lease {} is out of valid range for subnet {}", lease, subnet );
173             return null;
174         }
175 
176         // build properties map
177         Map properties = getProperties( subnet );
178 
179         // update lease options
180         OptionsField o = lease.getOptions();
181         o.clear();
182 
183         // add subnet settings
184         o.add( new SubnetMask( subnet.getNetmask() ) );
185         o.merge( subnet.getOptions() );
186 
187         // check whether there is a designated host.
188         Host host = findDesignatedHost( hardwareAddress );
189         if ( null != host )
190         {
191             // check whether the host matches the address (using a fixed
192             // host address is mandatory).
193             if ( host.getAddress() != null && !host.getAddress().equals( lease.getClientAddress() ) )
194             {
195                 LOG.warn( "Existing fixed address for " + hardwareAddress + " conflicts with existing lease "
196                     + lease );
197                 return null;
198             }
199 
200             properties.putAll( getProperties( host ) );
201 
202             // set (client) host name
203             o.add( new HostName( host.getName() ) );
204 
205             // add the host's options
206             o.merge( host.getOptions() );
207         }
208 
209         // update other lease fields
210         long leaseTime = determineLeaseTime( requestedLeaseTime, properties );
211         lease.setExpires( System.currentTimeMillis() + leaseTime );
212         lease.setHardwareAddress( hardwareAddress );
213 
214         // update the lease state
215         if ( lease.getState() != Lease.STATE_ACTIVE )
216         {
217             lease.setState( Lease.STATE_ACTIVE );
218             updateLease( lease );
219         }
220 
221         // store information about the lease
222         updateLease( lease );
223 
224         return lease;
225     }
226 
227 
228     /**
229      * Determine the lease time based on the time requested by the client, the
230      * properties and a global default.
231      * 
232      * @param requestedLeaseTime
233      * @param properties
234      * @return long
235      */
236     private long determineLeaseTime( long requestedLeaseTime, Map properties )
237     {
238         // built-in default
239         long leaseTime = 1000L * 3600;
240         Integer propMaxLeaseTime = ( Integer ) properties.get( DhcpConfigElement.PROPERTY_MAX_LEASE_TIME );
241 
242         if ( null != propMaxLeaseTime )
243         {
244             if ( requestedLeaseTime > 0 )
245             {
246                 leaseTime = Math.min( propMaxLeaseTime.intValue() * 1000L, requestedLeaseTime );
247             }
248             else
249             {
250                 leaseTime = propMaxLeaseTime.intValue() * 1000L;
251             }
252         }
253 
254         return leaseTime;
255     }
256 
257 
258     /*
259      * @see org.apache.directory.server.dhcp.store.DhcpStore#releaseLease(org.apache.directory.server.dhcp.service.Lease)
260      */
261     public void releaseLease( Lease lease )
262     {
263         lease.setState( Lease.STATE_RELEASED );
264         updateLease( lease );
265     }
266 
267 
268     /**
269      * Update the (possibly changed) lease in the store.
270      * 
271      * @param lease
272      */
273     protected abstract void updateLease( Lease lease );
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 OptionsField
285      */
286     protected abstract OptionsField getOptions( DhcpConfigElement element );
287 
288 
289     /**
290      * Return a list of all options applicable to the given config element. List
291      * list must contain the options specified for the element and all parent
292      * elements in an aggregated fashion. For instance, the options for a host
293      * must include the global default options, the options of classes the host
294      * is a member of, the host's group options and the host's options.
295      * 
296      * @param element
297      * @return Map
298      */
299     protected abstract Map getProperties( DhcpConfigElement element );
300 
301 
302     /**
303      * Find an existing lease in the store.
304      * 
305      * @param hardwareAddress
306      * @param existingLease
307      * @return Map
308      */
309     protected abstract Leaserver/dhcp/service/Lease.html#Lease">Lease findExistingLease( HardwareAddress hardwareAddress, Lease existingLease );
310 
311 
312     /**
313      * Find a host to with the explicitely designated hardware address.
314      * 
315      * @param hardwareAddress
316      * @return Host
317      * @throws DhcpException
318      */
319     protected abstract Host findDesignatedHost( HardwareAddress hardwareAddress ) throws DhcpException;
320 
321 
322     /**
323      * Find the subnet definition matching the given address.
324      * 
325      * @param clientAddress
326      * @return Subnet
327      */
328     protected abstract Subnet findSubnet( InetAddress clientAddress );
329 }