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.service;
21  
22  
23  import java.net.InetAddress;
24  import java.net.InetSocketAddress;
25  
26  import org.apache.directory.server.dhcp.DhcpException;
27  import org.apache.directory.server.dhcp.messages.DhcpMessage;
28  import org.apache.directory.server.dhcp.messages.MessageType;
29  import org.apache.directory.server.dhcp.options.AddressOption;
30  import org.apache.directory.server.dhcp.options.OptionsField;
31  import org.apache.directory.server.dhcp.options.dhcp.ClientIdentifier;
32  import org.apache.directory.server.dhcp.options.dhcp.IpAddressLeaseTime;
33  import org.apache.directory.server.dhcp.options.dhcp.MaximumDhcpMessageSize;
34  import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList;
35  import org.apache.directory.server.dhcp.options.dhcp.RequestedIpAddress;
36  import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier;
37  import org.apache.directory.server.dhcp.store.DhcpStore;
38  
39  
40  /**
41   * A default implementation of the DHCP service. Does the tedious low-level
42   * chores of handling DHCP messages, but delegates the lease-handling to a
43   * supplied DhcpStore.
44   * 
45   * @see org.apache.directory.server.dhcp.store.DhcpStore
46   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
47   */
48  public class StoreBasedDhcpService extends AbstractDhcpService
49  {
50      private final DhcpStore dhcpStore;
51  
52  
53      public StoreBasedDhcpService( DhcpStore dhcpStore )
54      {
55          this.dhcpStore = dhcpStore;
56      }
57  
58  
59      /**
60       * Try to get an existing lease. The lease may have been created during
61       * earlier DHCP negotiations or a recent DHCPDISCOVER.
62       * 
63       * @param clientAddress
64       * @param request
65       * @return
66       * @throws DhcpException
67       */
68      private Lease getExistingLease( InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException
69      {
70          // determine requested lease time
71          IpAddressLeaseTimerectory/server/dhcp/options/dhcp/IpAddressLeaseTime.html#IpAddressLeaseTime">IpAddressLeaseTime requestedLeaseTimeOption = ( IpAddressLeaseTime ) request.getOptions().get(
72              IpAddressLeaseTime.class );
73          long requestedLeaseTime = null != requestedLeaseTimeOption ? requestedLeaseTimeOption.getIntValue() * 1000
74              : -1L;
75  
76          // try to get the lease (address) requested by the client
77          InetAddress requestedAddress = null;
78          AddressOptionache/directory/server/dhcp/options/AddressOption.html#AddressOption">AddressOption requestedAddressOption = ( AddressOption ) request.getOptions().get( RequestedIpAddress.class );
79  
80          if ( null != requestedAddressOption )
81          {
82              requestedAddress = requestedAddressOption.getAddress();
83          }
84  
85          if ( null == requestedAddress )
86          {
87              requestedAddress = request.getCurrentClientAddress();
88          }
89  
90          InetAddress selectionBase = determineSelectionBase( clientAddress, request );
91  
92          Lease lease = dhcpStore.getExistingLease( request.getHardwareAddress(), requestedAddress, selectionBase,
93              requestedLeaseTime, request.getOptions() );
94  
95          if ( null == lease )
96          {
97              return null;
98          }
99  
100         return lease;
101     }
102 
103 
104     /**
105      * Determine a lease to offer in response to a DHCPDISCOVER message.
106      * <p>
107      * When a server receives a DHCPDISCOVER message from a client, the server
108      * chooses a network address for the requesting client. If no address is
109      * available, the server may choose to report the problem to the system
110      * administrator. If an address is available, the new address SHOULD be
111      * chosen as follows:
112      * <ul>
113      * <li> The client's current address as recorded in the client's current
114      * binding, ELSE
115      * <li> The client's previous address as recorded in the client's (now
116      * expired or released) binding, if that address is in the server's pool of
117      * available addresses and not already allocated, ELSE
118      * <li> The address requested in the 'Requested IP Address' option, if that
119      * address is valid and not already allocated, ELSE
120      * <li> A new address allocated from the server's pool of available
121      * addresses; the address is selected based on the subnet from which the
122      * message was received (if 'giaddr' is 0) or on the address of the relay
123      * agent that forwarded the message ('giaddr' when not 0).
124      * </ul>
125      * 
126      * @param clientAddress
127      * @param request
128      * @return
129      */
130     private Lease getLeaseOffer( InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException
131     {
132         // determine requested lease time
133         IpAddressLeaseTimerectory/server/dhcp/options/dhcp/IpAddressLeaseTime.html#IpAddressLeaseTime">IpAddressLeaseTime requestedLeaseTimeOption = ( IpAddressLeaseTime ) request.getOptions().get(
134             IpAddressLeaseTime.class );
135         long requestedLeaseTime = null != requestedLeaseTimeOption ? requestedLeaseTimeOption.getIntValue() * 1000
136             : -1L;
137 
138         // try to get the lease (address) requested by the client
139         InetAddress requestedAddress = null;
140         AddressOptionache/directory/server/dhcp/options/AddressOption.html#AddressOption">AddressOption requestedAddressOption = ( AddressOption ) request.getOptions().get( RequestedIpAddress.class );
141 
142         if ( null != requestedAddressOption )
143         {
144             requestedAddress = requestedAddressOption.getAddress();
145         }
146 
147         InetAddress selectionBase = determineSelectionBase( clientAddress, request );
148 
149         return dhcpStore.getLeaseOffer( request.getHardwareAddress(), requestedAddress, selectionBase,
150             requestedLeaseTime, request.getOptions() );
151     }
152 
153 
154     /*
155      * @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleRELEASE(java.net.InetSocketAddress,
156      *      java.net.InetSocketAddress,
157      *      org.apache.directory.server.dhcp.messages.DhcpMessage)
158      */
159     @Override
160     protected DhcpMessage handleRELEASE( InetSocketAddress localAddress, InetSocketAddress clientAddress,
161         DhcpMessage request ) throws DhcpException
162     {
163         // check server ident
164         AddressOptionrg/apache/directory/server/dhcp/options/AddressOption.html#AddressOption">AddressOption serverIdentOption = ( AddressOption ) request.getOptions().get( ServerIdentifier.class );
165 
166         if ( null != serverIdentOption && serverIdentOption.getAddress().isAnyLocalAddress() )
167         {
168             return null; // not me?! FIXME: handle authoritative server case
169         }
170 
171         Lease lease = getExistingLease( clientAddress, request );
172 
173         DhcpMessage reply = initGeneralReply( localAddress, request );
174 
175         if ( null == lease )
176         {
177             // null lease? send NAK
178             // FIXME...
179             reply.setMessageType( MessageType.DHCPNAK );
180             reply.setCurrentClientAddress( null );
181             reply.setAssignedClientAddress( null );
182             reply.setNextServerAddress( null );
183         }
184         else
185         {
186             dhcpStore.releaseLease( lease );
187 
188             // lease Ok, send ACK
189             // FIXME...
190             reply.getOptions().merge( lease.getOptions() );
191 
192             reply.setAssignedClientAddress( lease.getClientAddress() );
193             reply.setNextServerAddress( lease.getNextServerAddress() );
194 
195             // fix options
196             OptionsField options = reply.getOptions();
197 
198             // these options must not be present
199             options.remove( RequestedIpAddress.class );
200             options.remove( ParameterRequestList.class );
201             options.remove( ClientIdentifier.class );
202             options.remove( MaximumDhcpMessageSize.class );
203 
204             // these options must be present
205             options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );
206 
207             stripUnwantedOptions( request, options );
208         }
209 
210         return reply;
211 
212     }
213 
214 
215     /*
216      * @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleDISCOVER(java.net.InetSocketAddress,
217      *      org.apache.directory.server.dhcp.messages.DhcpMessage)
218      */
219     @Override
220     protected DhcpMessage handleDISCOVER( InetSocketAddress localAddress, InetSocketAddress clientAddress,
221         DhcpMessage request ) throws DhcpException
222     {
223         Lease lease = getLeaseOffer( clientAddress, request );
224 
225         // null lease? don't offer one.
226         if ( null == lease )
227         {
228             return null;
229         }
230 
231         DhcpMessage reply = initGeneralReply( localAddress, request );
232 
233         reply.getOptions().merge( lease.getOptions() );
234 
235         reply.setMessageType( MessageType.DHCPOFFER );
236 
237         reply.setAssignedClientAddress( lease.getClientAddress() );
238         reply.setNextServerAddress( lease.getNextServerAddress() );
239 
240         // fix options
241         OptionsField options = reply.getOptions();
242 
243         // these options must not be present
244         options.remove( RequestedIpAddress.class );
245         options.remove( ParameterRequestList.class );
246         options.remove( ClientIdentifier.class );
247         options.remove( MaximumDhcpMessageSize.class );
248 
249         // these options must be present
250         options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );
251 
252         stripUnwantedOptions( request, options );
253 
254         return reply;
255     }
256 
257 
258     /*
259      * @see org.apache.directory.server.dhcp.service.AbstractDhcpService#handleREQUEST(java.net.InetSocketAddress,
260      *      org.apache.directory.server.dhcp.messages.DhcpMessage)
261      */
262     @Override
263     protected DhcpMessage handleREQUEST( InetSocketAddress localAddress, InetSocketAddress clientAddress,
264         DhcpMessage request ) throws DhcpException
265     {
266         // check server ident
267         AddressOptionrg/apache/directory/server/dhcp/options/AddressOption.html#AddressOption">AddressOption serverIdentOption = ( AddressOption ) request.getOptions().get( ServerIdentifier.class );
268 
269         if ( null != serverIdentOption && serverIdentOption.getAddress().isAnyLocalAddress() )
270         {
271             return null; // not me?! FIXME: handle authoritative server case
272         }
273 
274         Lease lease = getExistingLease( clientAddress, request );
275 
276         DhcpMessage reply = initGeneralReply( localAddress, request );
277 
278         if ( null == lease )
279         {
280             // null lease? send NAK
281             reply.setMessageType( MessageType.DHCPNAK );
282             reply.setCurrentClientAddress( null );
283             reply.setAssignedClientAddress( null );
284             reply.setNextServerAddress( null );
285         }
286         else
287         {
288             // lease Ok, send ACK
289             reply.getOptions().merge( lease.getOptions() );
290 
291             reply.setAssignedClientAddress( lease.getClientAddress() );
292             reply.setNextServerAddress( lease.getNextServerAddress() );
293 
294             // fix options
295             OptionsField options = reply.getOptions();
296 
297             // these options must not be present
298             options.remove( RequestedIpAddress.class );
299             options.remove( ParameterRequestList.class );
300             options.remove( ClientIdentifier.class );
301             options.remove( MaximumDhcpMessageSize.class );
302 
303             // these options must be present
304             options.add( new IpAddressLeaseTime( ( lease.getExpires() - System.currentTimeMillis() ) / 1000L ) );
305 
306             stripUnwantedOptions( request, options );
307         }
308         return reply;
309     }
310 }