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.store; 021 022 023import java.net.InetAddress; 024import java.util.Map; 025 026import org.apache.directory.server.dhcp.DhcpException; 027import org.apache.directory.server.dhcp.messages.HardwareAddress; 028import org.apache.directory.server.dhcp.options.OptionsField; 029import org.apache.directory.server.dhcp.options.vendor.HostName; 030import org.apache.directory.server.dhcp.options.vendor.SubnetMask; 031import org.apache.directory.server.dhcp.service.Lease; 032import org.slf4j.Logger; 033import 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 */ 041public abstract class AbstractDhcpStore implements DhcpStore 042{ 043 private static final Logger LOG = LoggerFactory.getLogger( AbstractDhcpStore.class ); 044 045 046 /* 047 * @see org.apache.directory.server.dhcp.service.DhcpStore#getLeaseOffer(org.apache.directory.server.dhcp.messages.HardwareAddress, 048 * java.net.InetAddress, java.net.InetAddress, long, 049 * org.apache.directory.server.dhcp.options.OptionsField) 050 */ 051 public Lease getLeaseOffer( HardwareAddress hardwareAddress, InetAddress requestedAddress, 052 InetAddress selectionBase, long requestedLeaseTime, OptionsField options ) throws DhcpException 053 { 054 Subnet subnet = findSubnet( selectionBase ); 055 056 if ( null == subnet ) 057 { 058 LOG.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 066 if ( null != lease ) 067 { 068 return lease; 069 } 070 071 Host host = null; 072 host = findDesignatedHost( hardwareAddress ); 073 074 if ( null != host ) 075 { 076 // make sure that the host is actually within the subnet. Depending 077 // on the way the DhcpStore configuration is implemented, it is not 078 // possible to violate this condition, but we can't be sure. 079 if ( !subnet.contains( host.getAddress() ) ) 080 { 081 LOG.warn( "Host {} is not within the subnet for which an address is requested", host ); 082 } 083 else 084 { 085 // build properties map 086 Map properties = getProperties( subnet ); 087 properties.putAll( getProperties( host ) ); 088 089 // build lease 090 lease = new Lease(); 091 lease.setAcquired( System.currentTimeMillis() ); 092 093 long leaseTime = determineLeaseTime( requestedLeaseTime, properties ); 094 095 lease.setExpires( System.currentTimeMillis() + leaseTime ); 096 097 lease.setHardwareAddress( hardwareAddress ); 098 lease.setState( Lease.STATE_NEW ); 099 lease.setClientAddress( host.getAddress() ); 100 101 // set lease options 102 OptionsField o = lease.getOptions(); 103 104 // set (client) host name 105 o.add( new HostName( host.getName() ) ); 106 107 // add subnet settings 108 o.add( new SubnetMask( subnet.getNetmask() ) ); 109 o.merge( subnet.getOptions() ); 110 111 // add the host's options. they override existing 112 // subnet options as they take the precedence. 113 o.merge( host.getOptions() ); 114 } 115 } 116 117 // update the lease state 118 if ( null != lease && lease.getState() != Lease.STATE_ACTIVE ) 119 { 120 lease.setState( Lease.STATE_OFFERED ); 121 updateLease( lease ); 122 } 123 124 return lease; 125 } 126 127 128 /* 129 * @see org.apache.directory.server.dhcp.store.DhcpStore#getExistingLease(org.apache.directory.server.dhcp.messages.HardwareAddress, 130 * java.net.InetAddress, java.net.InetAddress, long, 131 * org.apache.directory.server.dhcp.options.OptionsField) 132 */ 133 public Lease getExistingLease( HardwareAddress hardwareAddress, InetAddress requestedAddress, 134 InetAddress selectionBase, long requestedLeaseTime, OptionsField options ) throws DhcpException 135 { 136 // try to find existing lease. if we don't find a lease based on the 137 // client's 138 // hardware address, we send a NAK. 139 Lease lease = null; 140 lease = findExistingLease( hardwareAddress, lease ); 141 142 if ( null == lease ) 143 { 144 return null; 145 } 146 147 // check whether the notions of the client address match 148 if ( !lease.getClientAddress().equals( requestedAddress ) ) 149 { 150 LOG.warn( "Requested address " + requestedAddress + " for " + hardwareAddress 151 + " doesn't match existing lease " + lease ); 152 return null; 153 } 154 155 // check whether addresses and subnet match 156 Subnet subnet = findSubnet( selectionBase ); 157 158 if ( null == subnet ) 159 { 160 LOG.warn( "No subnet found for existing lease {}", lease ); 161 return null; 162 } 163 164 if ( !subnet.contains( lease.getClientAddress() ) ) 165 { 166 LOG.warn( "Client with existing lease {} is on wrong subnet {}", lease, subnet ); 167 return null; 168 } 169 170 if ( !subnet.isInRange( lease.getClientAddress() ) ) 171 { 172 LOG.warn( "Client with existing lease {} is out of valid range for subnet {}", lease, subnet ); 173 return null; 174 } 175 176 // build properties map 177 Map properties = getProperties( subnet ); 178 179 // update lease options 180 OptionsField o = lease.getOptions(); 181 o.clear(); 182 183 // add subnet settings 184 o.add( new SubnetMask( subnet.getNetmask() ) ); 185 o.merge( subnet.getOptions() ); 186 187 // check whether there is a designated host. 188 Host host = findDesignatedHost( hardwareAddress ); 189 if ( null != host ) 190 { 191 // check whether the host matches the address (using a fixed 192 // host address is mandatory). 193 if ( host.getAddress() != null && !host.getAddress().equals( lease.getClientAddress() ) ) 194 { 195 LOG.warn( "Existing fixed address for " + hardwareAddress + " conflicts with existing lease " 196 + lease ); 197 return null; 198 } 199 200 properties.putAll( getProperties( host ) ); 201 202 // set (client) host name 203 o.add( new HostName( host.getName() ) ); 204 205 // add the host's options 206 o.merge( host.getOptions() ); 207 } 208 209 // update other lease fields 210 long leaseTime = determineLeaseTime( requestedLeaseTime, properties ); 211 lease.setExpires( System.currentTimeMillis() + leaseTime ); 212 lease.setHardwareAddress( hardwareAddress ); 213 214 // update the lease state 215 if ( lease.getState() != Lease.STATE_ACTIVE ) 216 { 217 lease.setState( Lease.STATE_ACTIVE ); 218 updateLease( lease ); 219 } 220 221 // store information about the lease 222 updateLease( lease ); 223 224 return lease; 225 } 226 227 228 /** 229 * Determine the lease time based on the time requested by the client, the 230 * properties and a global default. 231 * 232 * @param requestedLeaseTime 233 * @param properties 234 * @return long 235 */ 236 private long determineLeaseTime( long requestedLeaseTime, Map properties ) 237 { 238 // built-in default 239 long leaseTime = 1000L * 3600; 240 Integer propMaxLeaseTime = ( Integer ) properties.get( DhcpConfigElement.PROPERTY_MAX_LEASE_TIME ); 241 242 if ( null != propMaxLeaseTime ) 243 { 244 if ( requestedLeaseTime > 0 ) 245 { 246 leaseTime = Math.min( propMaxLeaseTime.intValue() * 1000L, requestedLeaseTime ); 247 } 248 else 249 { 250 leaseTime = propMaxLeaseTime.intValue() * 1000L; 251 } 252 } 253 254 return leaseTime; 255 } 256 257 258 /* 259 * @see org.apache.directory.server.dhcp.store.DhcpStore#releaseLease(org.apache.directory.server.dhcp.service.Lease) 260 */ 261 public void releaseLease( Lease lease ) 262 { 263 lease.setState( Lease.STATE_RELEASED ); 264 updateLease( lease ); 265 } 266 267 268 /** 269 * Update the (possibly changed) lease in the store. 270 * 271 * @param lease 272 */ 273 protected abstract void updateLease( Lease lease ); 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 OptionsField 285 */ 286 protected abstract OptionsField getOptions( DhcpConfigElement element ); 287 288 289 /** 290 * Return a list of all options applicable to the given config element. List 291 * list must contain the options specified for the element and all parent 292 * elements in an aggregated fashion. For instance, the options for a host 293 * must include the global default options, the options of classes the host 294 * is a member of, the host's group options and the host's options. 295 * 296 * @param element 297 * @return Map 298 */ 299 protected abstract Map getProperties( DhcpConfigElement element ); 300 301 302 /** 303 * Find an existing lease in the store. 304 * 305 * @param hardwareAddress 306 * @param existingLease 307 * @return Map 308 */ 309 protected abstract Lease findExistingLease( HardwareAddress hardwareAddress, Lease existingLease ); 310 311 312 /** 313 * Find a host to with the explicitely designated hardware address. 314 * 315 * @param hardwareAddress 316 * @return Host 317 * @throws DhcpException 318 */ 319 protected abstract Host findDesignatedHost( HardwareAddress hardwareAddress ) throws DhcpException; 320 321 322 /** 323 * Find the subnet definition matching the given address. 324 * 325 * @param clientAddress 326 * @return Subnet 327 */ 328 protected abstract Subnet findSubnet( InetAddress clientAddress ); 329}