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 }