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 }