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.service;
021
022
023 import java.net.InetAddress;
024 import java.net.InetSocketAddress;
025
026 import org.apache.directory.server.dhcp.DhcpException;
027 import org.apache.directory.server.dhcp.messages.DhcpMessage;
028 import org.apache.directory.server.dhcp.messages.MessageType;
029 import org.apache.directory.server.dhcp.options.AddressOption;
030 import org.apache.directory.server.dhcp.options.OptionsField;
031 import org.apache.directory.server.dhcp.options.dhcp.ClientIdentifier;
032 import org.apache.directory.server.dhcp.options.dhcp.IpAddressLeaseTime;
033 import org.apache.directory.server.dhcp.options.dhcp.MaximumDhcpMessageSize;
034 import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;
035 import org.apache.directory.server.dhcp.options.dhcp.RequestedIpAddress;
036 import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;
037 import org.apache.directory.server.dhcp.store.DhcpStore;
038
039
040 /**
041 * A default implementation of the DHCP service. Does the tedious low-level
042 * chores of handling DHCP messages, but delegates the lease-handling to a
043 * supplied DhcpStore.
044 *
045 * @see org.apache.directory.server.dhcp.store.DhcpStore
046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
047 * @version $Rev: 545042 $, $Date: 2007-06-06 22:32:01 -0500 (Mi, 06 Jun 2007) $
048 */
049 public class StoreBasedDhcpService extends AbstractDhcpService
050 {
051 private final DhcpStore dhcpStore;
052
053
054 public StoreBasedDhcpService(DhcpStore dhcpStore)
055 {
056 this.dhcpStore = dhcpStore;
057 }
058
059
060 /**
061 * Try to get an existing lease. The lease may have been created during
062 * earlier DHCP negotiations or a recent DHCPDISCOVER.
063 *
064 * @param clientAddress
065 * @param request
066 * @return
067 * @throws DhcpException
068 */
069 private Lease getExistingLease( InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException
070 {
071 // determine requested lease time
072 IpAddressLeaseTime requestedLeaseTimeOption = ( IpAddressLeaseTime ) request.getOptions().get(
073 IpAddressLeaseTime.class );
074 long requestedLeaseTime = null != requestedLeaseTimeOption ? requestedLeaseTimeOption.getIntValue() * 1000
075 : -1L;
076
077 // try to get the lease (address) requested by the client
078 InetAddress requestedAddress = null;
079 AddressOption requestedAddressOption = ( AddressOption ) request.getOptions().get( RequestedIpAddress.class );
080 if ( null != requestedAddressOption )
081 requestedAddress = requestedAddressOption.getAddress();
082 if ( null == requestedAddress )
083 requestedAddress = request.getCurrentClientAddress();
084
085 InetAddress selectionBase = determineSelectionBase( clientAddress, request );
086
087 Lease lease = dhcpStore.getExistingLease( request.getHardwareAddress(), requestedAddress, selectionBase,
088 requestedLeaseTime, request.getOptions() );
089
090 if ( null == lease )
091 return null;
092
093 return lease;
094 }
095
096
097 /**
098 * Determine a lease to offer in response to a DHCPDISCOVER message.
099 * <p>
100 * When a server receives a DHCPDISCOVER message from a client, the server
101 * chooses a network address for the requesting client. If no address is
102 * available, the server may choose to report the problem to the system
103 * administrator. If an address is available, the new address SHOULD be
104 * chosen as follows:
105 * <ul>
106 * <li> The client's current address as recorded in the client's current
107 * binding, ELSE
108 * <li> The client's previous address as recorded in the client's (now
109 * expired or released) binding, if that address is in the server's pool of
110 * available addresses and not already allocated, ELSE
111 * <li> The address requested in the 'Requested IP Address' option, if that
112 * address is valid and not already allocated, ELSE
113 * <li> A new address allocated from the server's pool of available
114 * addresses; the address is selected based on the subnet from which the
115 * message was received (if 'giaddr' is 0) or on the address of the relay
116 * agent that forwarded the message ('giaddr' when not 0).
117 * </ul>
118 *
119 * @param clientAddress
120 * @param request
121 * @return
122 */
123 private Lease getLeaseOffer( InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException
124 {
125 // determine requested lease time
126 IpAddressLeaseTime requestedLeaseTimeOption = ( IpAddressLeaseTime ) request.getOptions().get(
127 IpAddressLeaseTime.class );
128 long requestedLeaseTime = null != requestedLeaseTimeOption ? requestedLeaseTimeOption.getIntValue() * 1000
129 : -1L;
130
131 // try to get the lease (address) requested by the client
132 InetAddress requestedAddress = null;
133 AddressOption requestedAddressOption = ( AddressOption ) request.getOptions().get( RequestedIpAddress.class );
134 if ( null != requestedAddressOption )
135 requestedAddress = requestedAddressOption.getAddress();
136
137 InetAddress selectionBase = determineSelectionBase( clientAddress, request );
138
139 Lease lease = dhcpStore.getLeaseOffer( request.getHardwareAddress(), requestedAddress, selectionBase,
140 requestedLeaseTime, request.getOptions() );
141
142 return lease;
143 }
144
145
146 /*
147 * @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleRELEASE(java.net.InetSocketAddress,
148 * java.net.InetSocketAddress,
149 * org.apache.directory.server.dhcp.messages.DhcpMessage)
150 */
151 protected DhcpMessage handleRELEASE( InetSocketAddress localAddress, InetSocketAddress clientAddress,
152 DhcpMessage request ) throws DhcpException
153 {
154 // check server ident
155 AddressOption serverIdentOption = ( AddressOption ) request.getOptions().get( ServerIdentifier.class );
156 if ( null != serverIdentOption && serverIdentOption.getAddress().isAnyLocalAddress() )
157 return null; // not me?! FIXME: handle authoritative server case
158
159 Lease lease = getExistingLease( clientAddress, request );
160
161 DhcpMessage reply = initGeneralReply( localAddress, request );
162
163 if ( null == lease )
164 {
165 // null lease? send NAK
166 // FIXME...
167 reply.setMessageType( MessageType.DHCPNAK );
168 reply.setCurrentClientAddress( null );
169 reply.setAssignedClientAddress( null );
170 reply.setNextServerAddress( null );
171 }
172 else
173 {
174 dhcpStore.releaseLease( lease );
175
176 // lease Ok, send ACK
177 // FIXME...
178 reply.getOptions().merge( lease.getOptions() );
179
180 reply.setAssignedClientAddress( lease.getClientAddress() );
181 reply.setNextServerAddress( lease.getNextServerAddress() );
182
183 // fix options
184 OptionsField options = reply.getOptions();
185
186 // these options must not be present
187 options.remove( RequestedIpAddress.class );
188 options.remove( ParameterRequestList.class );
189 options.remove( ClientIdentifier.class );
190 options.remove( MaximumDhcpMessageSize.class );
191
192 // these options must be present
193 options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );
194
195 stripUnwantedOptions( request, options );
196 }
197 return reply;
198
199 }
200
201
202 /*
203 * @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleDISCOVER(java.net.InetSocketAddress,
204 * org.apache.directory.server.dhcp.messages.DhcpMessage)
205 */
206 protected DhcpMessage handleDISCOVER( InetSocketAddress localAddress, InetSocketAddress clientAddress,
207 DhcpMessage request ) throws DhcpException
208 {
209 Lease lease = getLeaseOffer( clientAddress, request );
210
211 // null lease? don't offer one.
212 if ( null == lease )
213 return null;
214
215 DhcpMessage reply = initGeneralReply( localAddress, request );
216
217 reply.getOptions().merge( lease.getOptions() );
218
219 reply.setMessageType( MessageType.DHCPOFFER );
220
221 reply.setAssignedClientAddress( lease.getClientAddress() );
222 reply.setNextServerAddress( lease.getNextServerAddress() );
223
224 // fix options
225 OptionsField options = reply.getOptions();
226
227 // these options must not be present
228 options.remove( RequestedIpAddress.class );
229 options.remove( ParameterRequestList.class );
230 options.remove( ClientIdentifier.class );
231 options.remove( MaximumDhcpMessageSize.class );
232
233 // these options must be present
234 options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );
235
236 stripUnwantedOptions( request, options );
237
238 return reply;
239 }
240
241
242 /*
243 * @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleREQUEST(java.net.InetSocketAddress,
244 * org.apache.directory.server.dhcp.messages.DhcpMessage)
245 */
246 protected DhcpMessage handleREQUEST( InetSocketAddress localAddress, InetSocketAddress clientAddress,
247 DhcpMessage request ) throws DhcpException
248 {
249 // check server ident
250 AddressOption serverIdentOption = ( AddressOption ) request.getOptions().get( ServerIdentifier.class );
251 if ( null != serverIdentOption && serverIdentOption.getAddress().isAnyLocalAddress() )
252 return null; // not me?! FIXME: handle authoritative server case
253
254 Lease lease = getExistingLease( clientAddress, request );
255
256 DhcpMessage reply = initGeneralReply( localAddress, request );
257
258 if ( null == lease )
259 {
260 // null lease? send NAK
261 reply.setMessageType( MessageType.DHCPNAK );
262 reply.setCurrentClientAddress( null );
263 reply.setAssignedClientAddress( null );
264 reply.setNextServerAddress( null );
265 }
266 else
267 {
268 // lease Ok, send ACK
269 reply.getOptions().merge( lease.getOptions() );
270
271 reply.setAssignedClientAddress( lease.getClientAddress() );
272 reply.setNextServerAddress( lease.getNextServerAddress() );
273
274 // fix options
275 OptionsField options = reply.getOptions();
276
277 // these options must not be present
278 options.remove( RequestedIpAddress.class );
279 options.remove( ParameterRequestList.class );
280 options.remove( ClientIdentifier.class );
281 options.remove( MaximumDhcpMessageSize.class );
282
283 // these options must be present
284 options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );
285
286 stripUnwantedOptions( request, options );
287 }
288 return reply;
289 }
290 }