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 */ 020package org.apache.directory.server.dhcp.service; 021 022 023import java.net.InetAddress; 024import java.net.InetSocketAddress; 025 026import org.apache.directory.server.dhcp.DhcpException; 027import org.apache.directory.server.dhcp.messages.DhcpMessage; 028import org.apache.directory.server.dhcp.messages.MessageType; 029import org.apache.directory.server.dhcp.options.AddressOption; 030import org.apache.directory.server.dhcp.options.OptionsField; 031import org.apache.directory.server.dhcp.options.dhcp.ClientIdentifier; 032import org.apache.directory.server.dhcp.options.dhcp.IpAddressLeaseTime; 033import org.apache.directory.server.dhcp.options.dhcp.MaximumDhcpMessageSize; 034import org.apache.directory.server.dhcp.options.dhcp.ParameterRequestList; 035import org.apache.directory.server.dhcp.options.dhcp.RequestedIpAddress; 036import org.apache.directory.server.dhcp.options.dhcp.ServerIdentifier; 037import 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 */ 048public class StoreBasedDhcpService extends AbstractDhcpService 049{ 050 private final DhcpStore dhcpStore; 051 052 053 public StoreBasedDhcpService( DhcpStore dhcpStore ) 054 { 055 this.dhcpStore = dhcpStore; 056 } 057 058 059 /** 060 * Try to get an existing lease. The lease may have been created during 061 * earlier DHCP negotiations or a recent DHCPDISCOVER. 062 * 063 * @param clientAddress 064 * @param request 065 * @return 066 * @throws DhcpException 067 */ 068 private Lease getExistingLease( InetSocketAddress clientAddress, DhcpMessage request ) throws DhcpException 069 { 070 // determine requested lease time 071 IpAddressLeaseTime requestedLeaseTimeOption = ( IpAddressLeaseTime ) request.getOptions().get( 072 IpAddressLeaseTime.class ); 073 long requestedLeaseTime = null != requestedLeaseTimeOption ? requestedLeaseTimeOption.getIntValue() * 1000 074 : -1L; 075 076 // try to get the lease (address) requested by the client 077 InetAddress requestedAddress = null; 078 AddressOption requestedAddressOption = ( AddressOption ) request.getOptions().get( RequestedIpAddress.class ); 079 080 if ( null != requestedAddressOption ) 081 { 082 requestedAddress = requestedAddressOption.getAddress(); 083 } 084 085 if ( null == requestedAddress ) 086 { 087 requestedAddress = request.getCurrentClientAddress(); 088 } 089 090 InetAddress selectionBase = determineSelectionBase( clientAddress, request ); 091 092 Lease lease = dhcpStore.getExistingLease( request.getHardwareAddress(), requestedAddress, selectionBase, 093 requestedLeaseTime, request.getOptions() ); 094 095 if ( null == lease ) 096 { 097 return null; 098 } 099 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 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 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 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 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}