/* * Copyright 2009-2014 Jagornet Technologies, LLC. All Rights Reserved. * * This software is the proprietary information of Jagornet Technologies, LLC. * Use is subject to license terms. * */ /* * This file BaseBindingManager.java is part of Jagornet DHCP. * * Jagornet DHCP is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Jagornet DHCP is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jagornet DHCP. If not, see <http://www.gnu.org/licenses/>. * */ package com.jagornet.dhcp.server.request.binding; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.Timer; import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jagornet.dhcp.db.IaAddress; import com.jagornet.dhcp.db.IaManager; import com.jagornet.dhcp.db.IdentityAssoc; import com.jagornet.dhcp.message.DhcpMessage; import com.jagornet.dhcp.server.config.DhcpConfigObject; import com.jagornet.dhcp.server.config.DhcpLink; import com.jagornet.dhcp.server.config.DhcpServerConfigException; import com.jagornet.dhcp.server.config.DhcpServerConfiguration; import com.jagornet.dhcp.util.DhcpConstants; import com.jagornet.dhcp.util.Subnet; import com.jagornet.dhcp.xml.Link; import com.jagornet.dhcp.xml.LinkFilter; /** * The Class BaseBindingManager. * Top-level abstract class used by all BindingManagers. * * @author A. Gregory Rabil */ public abstract class BaseBindingManager { private static Logger log = LoggerFactory.getLogger(BaseBindingManager.class); protected static DhcpServerConfiguration serverConfig = DhcpServerConfiguration.getInstance(); protected IaManager iaMgr; /** * The map of binding binding pools for this manager. The key is the link address * and the value is the list of configured BindingPools for the link. */ protected Map<String, List<? extends BindingPool>> bindingPoolMap; /** * The map of static bindings for this manager. The key is the link address * and the value is the list of configured StaticBindings for the link. */ protected Map<String, List<? extends StaticBinding>> staticBindingMap; /** The reaper thread for cleaning expired bindings. */ protected Timer reaper; /** The lock for access to the reuseAvailableAddress method */ // the ReentrantLock class is better than synchronized private final ReentrantLock lock = new ReentrantLock(); /** * Initialize the manager. Read the configuration and build * the pool map and static bindings. * @throws DhcpServerConfigException */ public void init() throws DhcpServerConfigException { initPoolMap(); initStaticBindings(); startReaper(); } /** * Initialize the pool map. Read through the link map from the server's * configuration and build the pool map keyed by link address with a * value of the list of (na/ta/v4 address or prefix) bindings for the link. * * @throws DhcpServerConfigException the exception */ protected void initPoolMap() throws DhcpServerConfigException { SortedMap<Subnet, DhcpLink> linkMap = serverConfig.getLinkMap(); if ((linkMap != null) && !linkMap.isEmpty()) { bindingPoolMap = new HashMap<String, List<? extends BindingPool>>(); for (DhcpLink dhcpLink : linkMap.values()) { List<? extends BindingPool> bindingPools = buildBindingPools(dhcpLink.getLink()); if ((bindingPools != null) && !bindingPools.isEmpty()) { bindingPoolMap.put(dhcpLink.getLinkAddress(), bindingPools); } } } else { log.error("LinkMap is null for DhcpServerConfiguration"); } } /** * Build the list of BindingPools for the given DhcpLink. The BindingPools * are either V6AddressBindingPools (NA and TA) or V6PrefixBindingPools * or V4AddressBindingPools. * * @param link the configured DhcpLink * @return the list of BindingPools for the link * @throws DhcpServerConfigException */ protected abstract List<? extends BindingPool> buildBindingPools(Link link) throws DhcpServerConfigException; /** * Initialize the static bindings. Read through the link map from the server's * configuration and build the binding map keyed by link address with a * value of the list of (na/ta/v4 address or prefix) bindings for the link. * * * @throws DhcpServerConfigException the exception */ protected void initStaticBindings() throws DhcpServerConfigException { SortedMap<Subnet, DhcpLink> linkMap = serverConfig.getLinkMap(); if (linkMap != null) { staticBindingMap = new HashMap<String, List<? extends StaticBinding>>(); for (DhcpLink dhcpLink : linkMap.values()) { List<? extends StaticBinding> staticBindings = buildStaticBindings(dhcpLink.getLink()); if ((staticBindings != null) && !staticBindings.isEmpty()) { staticBindingMap.put(dhcpLink.getLinkAddress(), staticBindings); } } } } /** * Initialize the static bindings for the given DhcpLink. The StaticBindings * are either StaticAddressBindings (NA and TA) or StaticPrefixBindings * or StaticV4AddressBindings. * * @param link the configured DhcpLink * @return the list of StaticBindings for the link * @throws DhcpServerConfigException */ protected abstract List<? extends StaticBinding> buildStaticBindings(Link link) throws DhcpServerConfigException; /** * Start a reaper thread to check for expired bindings. */ protected abstract void startReaper(); /** * Find binding pool for address in a message received on the given link. * * @param link the link on which the client message was received. * @param inetAddr the IP address referenced in the message * @param requestMsg the request message * * @return the binding pool */ protected BindingPool findBindingPool(Link link, InetAddress inetAddr, DhcpMessage requestMsg) { List<? extends BindingPool> bps = bindingPoolMap.get(link.getAddress()); if ((bps != null) && !bps.isEmpty()) { for (BindingPool bindingPool : bps) { // if (log.isDebugEnabled()) { // if (bindingPool instanceof AddressBindingPool) { // AddressBindingPool abp = (AddressBindingPool) bindingPool; // log.debug("AddressBindingPool: " + abp.toString()); // log.debug("FreeList: " + abp.freeListToString()); // } // } if (bindingPool.contains(inetAddr)) { if ((requestMsg != null) && (bindingPool.getLinkFilter() != null)) { if (DhcpServerConfiguration.msgMatchesFilter(requestMsg, bindingPool.getLinkFilter())) { log.info("Found filter binding pool: " + bindingPool); return bindingPool; } } else { log.info("Found binding pool: " + bindingPool); return bindingPool; } } } } return null; } /** * Find binding pool for the given IP address. Search through all * the pools on all links to find the IP's binding pool. * * @param inetAddr the IP address * * @return the binding pool */ protected BindingPool findBindingPool(InetAddress inetAddr) { Collection<List<? extends BindingPool>> allPools = bindingPoolMap.values(); if ((allPools != null) && !allPools.isEmpty()) { for (List<? extends BindingPool> bps : allPools) { for (BindingPool bindingPool : bps) { if (bindingPool.contains(inetAddr)) { log.info("Found binding pool for address=" + inetAddr.getHostAddress() + ": " + bindingPool); return bindingPool; } } } } return null; } /** * Sets an IP address as in-use in it's binding pool. * * @param link the link for the message * @param inetAddr the IP address to set used */ protected void setIpAsUsed(Link link, InetAddress inetAddr) { BindingPool bindingPool = findBindingPool(link, inetAddr, null); if (bindingPool != null) { bindingPool.setUsed(inetAddr); } else { log.warn("Unable to set address used: No BindingPool found for IP=" + inetAddr.getHostAddress()); } } /** * Sets and IP address as free in it's binding pool. * * @param inetAddr the IP address to free */ protected void freeAddress(InetAddress inetAddr) { BindingPool bp = findBindingPool(inetAddr); if (bp != null) { bp.setFree(inetAddr); } else { log.error("Failed to free address: no BindingPool found for address: " + inetAddr.getHostAddress()); } } /** * Find the current binding, if any, for the given client identity association (IA). * * @param clientLink the link for the client request message * @param duid the DUID of the client * @param iatype the IA type of the client request * @param iaid the IAID of the client request * @param requestMsg the client request message * @return the existing Binding for this client request */ protected Binding findCurrentBinding(DhcpLink clientLink, byte[] duid, byte iatype, long iaid, DhcpMessage requestMsg) { Binding binding = null; try { IdentityAssoc ia = iaMgr.findIA(duid, iatype, iaid); if (ia != null) { log.info("Found current binding for " + IdentityAssoc.keyToString(duid, iatype, iaid) + " state=" + ia.getState()); binding = buildBindingFromIa(ia, clientLink, requestMsg); if (binding != null) { log.info("Successfully built Binding object: " + binding); } else { log.error("Failed to build Binding object"); } } else { log.info("No current binding found for " + IdentityAssoc.keyToString(duid, iatype, iaid)); } } catch (Exception ex) { log.error("Failed to find current binding", ex); } return binding; } /** * Find a static binding, if any, for the given client identity association (IA). * * @param clientLink the link for the client request message * @param duid the DUID of the client * @param iatype the IA type of the client request * @param iaid the IAID of the client request * @param requestMsg the client request message * @return the existing StaticBinding for this client request */ public StaticBinding findStaticBinding(Link clientLink, byte[] duid, byte iatype, long iaid, DhcpMessage requestMsg) { try { List<? extends StaticBinding> staticBindings = staticBindingMap.get(clientLink.getAddress()); if ((staticBindings != null) && !staticBindings.isEmpty()) { for (StaticBinding staticBinding : staticBindings) { if (staticBinding.matches(duid, iatype, iaid, requestMsg)) { log.info("Found static binding: " + staticBinding); return staticBinding; } } } } catch (Exception ex) { log.error("Exception in findStaticBinding", ex); } return null; } /** * Create a binding in response to a Solicit/Discover request for the given client IA. * * @param clientLink the link for the client request message * @param duid the DUID of the client * @param iatype the IA type of the client request * @param iaid the IAID of the client request * @param requestAddrs the list of requested IP addresses, if any * @param requestMsg the client request message * @param state the binding state * @return the created Binding */ protected Binding createBinding(DhcpLink clientLink, byte[] duid, byte iatype, long iaid, List<InetAddress> requestAddrs, DhcpMessage requestMsg, byte state) { Binding binding = null; log.debug("Getting addresses for new binding"); List<InetAddress> inetAddrs = getInetAddrs(clientLink, duid, iatype, iaid, requestAddrs, requestMsg); if ((inetAddrs != null) && !inetAddrs.isEmpty()) { log.debug("Got " + inetAddrs.size() + " addresses, building binding"); binding = buildBinding(clientLink, duid, iatype, iaid, state); log.debug("Building binding objects"); Set<BindingObject> bindingObjs = buildBindingObjects(clientLink, inetAddrs, requestMsg, state); if ((bindingObjs != null) && !bindingObjs.isEmpty()) { binding.setBindingObjects(bindingObjs); log.info("Creating new binding"); try { iaMgr.createIA(binding); } catch (Exception ex) { log.error("Failed to create persistent binding", ex); return null; } } else { log.error("Failed to build binding object(s)"); return null; } } String bindingType = (iatype == IdentityAssoc.V4_TYPE) ? "discover" : "solicit"; if (binding != null) { log.info("Created " + bindingType + " binding: " + binding.toString()); } else { log.warn("Failed to create " + bindingType + " binding"); } return binding; } /** * Create a binding in from a StaticBinding * * @param clientLink the link for the client request message * @param duid the DUID of the client * @param iatype the IA type of the client request * @param iaid the IAID of the client request * @param staticBinding the static binding * @param requestMsg the client request message * @return the created Binding */ protected Binding createStaticBinding(DhcpLink clientLink, byte[] duid, byte iatype, long iaid, StaticBinding staticBinding, DhcpMessage requestMsg) { Binding binding = null; if (staticBinding != null) { binding = buildBinding(clientLink, duid, iatype, iaid, IaAddress.STATIC); InetAddress inetAddr = staticBinding.getInetAddress(); if (inetAddr != null) { IaAddress iaAddr = new IaAddress(); iaAddr.setIpAddress(inetAddr); iaAddr.setState(IaAddress.STATIC); V6BindingAddress bindingAddr = new V6BindingAddress(iaAddr, staticBinding); setBindingObjectTimes(bindingAddr, staticBinding.getPreferredLifetimeMs(), staticBinding.getPreferredLifetimeMs()); // TODO store the configured options in the persisted binding? // bindingAddr.setDhcpOptions(bp.getDhcpOptions()); Collection<BindingObject> bindingObjs = new ArrayList<BindingObject>(); bindingObjs.add(bindingAddr); binding.setBindingObjects(bindingObjs); try { iaMgr.createIA(binding); } catch (Exception ex) { log.error("Failed to create persistent binding", ex); return null; } } else { log.error("Failed to build binding object(s)"); return null; } } else { log.error("StaticBinding object is null"); } String bindingType = (iatype == IdentityAssoc.V4_TYPE) ? "discover" : "solicit"; if (binding != null) { log.info("Created static " + bindingType + " binding: " + binding.toString()); } else { log.warn("Failed to create static " + bindingType + " binding"); } return binding; } /** * Update an existing client binding. Addresses in the current binding that are appropriate * for the client's link are simply updated with new lifetimes. If no current bindings are * appropriate for the client link, new binding addresses will be created and added to the * existing binding, leaving any other addresses alone. * * @param binding the existing client binding * @param clientLink the link for the client request message * @param duid the DUID of the client * @param iatype the IA type of the client request * @param iaid the IAID of the client request * @param requestAddrs the list of requested IP addresses, if any * @param requestMsg the client request message * @param state the new state for the binding * @return the updated Binding */ protected Binding updateBinding(Binding binding, DhcpLink clientLink, byte[] duid, byte iatype, long iaid, List<InetAddress> requestAddrs, DhcpMessage requestMsg, byte state) { Collection<? extends IaAddress> addIaAddresses = null; Collection<? extends IaAddress> updateIaAddresses = null; Collection<? extends IaAddress> delIaAddresses = null; // not used currently // log.info("Updating dynamic binding: " + binding); Collection<BindingObject> bindingObjs = binding.getBindingObjects(); if ((bindingObjs != null) && !bindingObjs.isEmpty()) { // current binding has addresses, so update times setBindingObjsTimes(bindingObjs); // the existing IaAddress binding objects will be updated updateIaAddresses = binding.getIaAddresses(); } else { log.warn("Existing binding has no on-link addresses, allocating new address(es)"); // current binding has no addresses, add new one(s) List<InetAddress> inetAddrs = getInetAddrs(clientLink, duid, iatype, iaid, requestAddrs, requestMsg); if ((inetAddrs == null) || inetAddrs.isEmpty()) { log.error("Failed to update binding, no addresses available"); return null; } bindingObjs = buildBindingObjects(clientLink, inetAddrs, requestMsg, state); binding.setBindingObjects(bindingObjs); // these new IaAddress binding objects will be added addIaAddresses = binding.getIaAddresses(); } binding.setState(state); try { log.info("Updating binding"); iaMgr.updateIA(binding, addIaAddresses, updateIaAddresses, delIaAddresses); log.info("Binding updated: " + binding.toString()); return binding; // if we get here, it worked } catch (Exception ex) { log.error("Failed to update binding", ex); return null; } } /** * Update an existing static binding. * * @param binding the existing client binding * @param clientLink the link for the client request message * @param duid the DUID of the client * @param iatype the IA type of the client request * @param iaid the IAID of the client request * @param staticBinding the static binding * @param requestMsg the client request message * @return the updated Binding */ protected Binding updateStaticBinding(Binding binding, DhcpLink clientLink, byte[] duid, byte iatype, long iaid, StaticBinding staticBinding, DhcpMessage requestMsg) { Collection<? extends IaAddress> addIaAddresses = null; Collection<? extends IaAddress> updateIaAddresses = null; Collection<? extends IaAddress> delIaAddresses = null; // not used currently if (staticBinding != null) { log.info("Updating static binding: " + binding); Collection<BindingObject> bindingObjs = binding.getBindingObjects(); if ((bindingObjs != null) && !bindingObjs.isEmpty()) { for (BindingObject bindingObj : bindingObjs) { if (bindingObj.getIpAddress().equals(staticBinding.getInetAddress())) { setBindingObjectTimes(bindingObj, staticBinding.getPreferredLifetimeMs(), staticBinding.getPreferredLifetimeMs()); break; } //TODO: what about bindingObjs that do NOT match the static binding? } // the existing IaAddress binding objects will be updated updateIaAddresses = binding.getIaAddresses(); } else { InetAddress inetAddr = staticBinding.getInetAddress(); if (inetAddr != null) { BindingObject bindingObj = buildStaticBindingObject(inetAddr, staticBinding); bindingObjs = new ArrayList<BindingObject>(); bindingObjs.add(bindingObj); binding.setBindingObjects(bindingObjs); // this new IaAddress binding object will be added addIaAddresses = binding.getIaAddresses(); } } } else { log.error("StaticBindingObject is null"); } binding.setState(IaAddress.STATIC); try { iaMgr.updateIA(binding, addIaAddresses, updateIaAddresses, delIaAddresses); return binding; // if we get here, it worked } catch (Exception ex) { log.error("Failed to update binding", ex); return null; } } /** * Get list of IP addresses for the given client IA request. * * @param clientLink the link for the client request message * @param duid the DUID of the client * @param iatype the IA type of the client request * @param iaid the IAID of the client request * @param requestAddrs the list of requested IP addresses, if any * @param requestMsg the client request message * @return the list of InetAddresses */ protected List<InetAddress> getInetAddrs(DhcpLink clientLink, byte[] duid, byte iatype, long iaid, List<InetAddress> requestAddrs, DhcpMessage requestMsg) { List<InetAddress> inetAddrs = new ArrayList<InetAddress>(); if ((requestAddrs != null) && !requestAddrs.isEmpty() ) { for (InetAddress reqAddr : requestAddrs) { if (!reqAddr.equals(DhcpConstants.ZEROADDR_V6)) { BindingPool bp = findBindingPool(clientLink.getLink(), reqAddr, requestMsg); if (bp == null) { log.warn("No BindingPool found for requested client address: " + reqAddr.getHostAddress()); if (iatype == IdentityAssoc.PD_TYPE) { // TAHI tests want NoPrefixAvail in this case log.warn("Requested prefix is not available, returning"); return inetAddrs; } // if there is no pool for the requested address, then skip it // because that address is either off-link or no longer valid continue; } log.info("Searching existing bindings for requested IP=" + reqAddr.getHostAddress()); IdentityAssoc ia = null; try { ia = iaMgr.findIA(reqAddr); if (ia != null) { // the address is assigned to an IA, which we // don't expect to be this IA, because we would // have found it using findCurrentBinding... // but, perhaps another thread just created it? if (isMyIa(duid, iatype, iaid, ia)) { log.warn("Requested IP=" + reqAddr.getHostAddress() + " is already held by THIS client " + IdentityAssoc.keyToString(duid, iatype, iaid) + ". Allowing this requested IP."); } else { log.info("Requested IP=" + reqAddr.getHostAddress() + " is held by ANOTHER client " + IdentityAssoc.keyToString(duid, iatype, iaid)); // the address is held by another IA, so get a new one reqAddr = getNextFreeAddress(clientLink, requestMsg); } } if (reqAddr != null) { inetAddrs.add(reqAddr); } } catch (Exception ex) { log.error("Failure finding IA for address", ex); } } } } if (inetAddrs.isEmpty()) { // the client did not request any valid addresses, so get the next one InetAddress inetAddr = getNextFreeAddress(clientLink, requestMsg); if (inetAddr != null) { inetAddrs.add(inetAddr); } } return inetAddrs; } /** * Checks if the duid-iatype-iaid tuple matches the given IA. * * @param duid the DUID * @param iatype the IA type * @param iaid the IAID * @param ia the IA * * @return true, if is my ia */ protected boolean isMyIa(byte[] duid, byte iatype, long iaid, IdentityAssoc ia) { boolean rc = false; if (duid != null) { if (Arrays.equals(ia.getDuid(), duid) && (ia.getIatype() == iatype) && (ia.getIaid() == iaid)) { rc = true; } } return rc; } /** * Gets the next free address from the pool(s) configured for the client link. * * @param clientLink the client link * @param requestMsg the request message * * @return the next free address */ protected InetAddress getNextFreeAddress(DhcpLink clientLink, DhcpMessage requestMsg) { if (clientLink != null) { List<? extends BindingPool> pools = bindingPoolMap.get(clientLink.getLinkAddress()); if ((pools != null) && !pools.isEmpty()) { for (BindingPool bp : pools) { LinkFilter filter = bp.getLinkFilter(); if ((requestMsg != null) && (filter != null)) { if (!DhcpServerConfiguration.msgMatchesFilter(requestMsg, filter)) { log.info("Client request does not match filter, skipping pool: " + bp.toString()); continue; } } if (log.isDebugEnabled()) log.debug("Getting next available address from pool: " + bp.toString()); InetAddress free = bp.getNextAvailableAddress(); if (free != null) { if (log.isDebugEnabled()) log.debug("Found next available address: " + free.getHostAddress()); return free; } else { // warning here, b/c there may be more pools log.warn("No free addresses available in pool: " + bp.toString()); } } // if we get this far, then we did not find any free(virgin) addresses // so we must start searching for any that can be used in the pools for (BindingPool bp : pools) { LinkFilter filter = bp.getLinkFilter(); if ((requestMsg != null) && (filter != null)) { if (!DhcpServerConfiguration.msgMatchesFilter(requestMsg, filter)) { log.info("Client request does not match filter, skipping pool: " + bp.toString()); continue; } } InetAddress reused = reuseAvailableAddress(bp); if (reused != null) { return reused; } } } else { log.error("No Pools defined in server configuration for Link: " + clientLink.getLinkAddress()); } } else { throw new IllegalStateException("ClientLink is null"); } return null; } /** * Find an address that can be reused. This method is invoked only * when no "virgin" leases can be found for a new client request. * * @param bp the binding pool * @return the oldest available address, if any */ protected InetAddress reuseAvailableAddress(BindingPool bp) { lock.lock(); try { if (log.isDebugEnabled()) log.debug("Finding available addresses in pool: " + bp.toString()); List<IaAddress> iaAddrs = iaMgr.findUnusedIaAddresses(bp.getStartAddress(), bp.getEndAddress()); if ((iaAddrs != null) && !iaAddrs.isEmpty()) { if (log.isDebugEnabled()) { for (IaAddress iaAddr : iaAddrs) { log.debug("Found available address: " + iaAddr.toString()); } } // list is ordered by validendtime // so the first one is the oldest one IaAddress iaAddr = iaAddrs.get(0); log.info("Deleting oldest available address: " + iaAddr.toString()); // delete the oldest one and return the IP // allowing that IP to be used again iaMgr.deleteIaAddr(iaAddr); return iaAddr.getIpAddress(); // TODO: should we clear the rest of unused IPs // now, or wait for them to expire or be needed // for (int i=1; i<iaAddrs.size(); i++) { // // } } } finally { lock.unlock(); } return null; } /** * Create a Binding given an IdentityAssoc loaded from the database. * * @param ia the IA * @param clientLink the client link * @param requestMsg the request message * * @return the binding */ protected abstract Binding buildBindingFromIa(IdentityAssoc ia, DhcpLink clientLink, DhcpMessage requestMsg); /** * Builds a new Binding. Create a new IdentityAssoc from the given * tuple and wrap that IdentityAssoc in a Binding. * * @param clientLink the client link * @param duid the DUID * @param iatype the IA type * @param iaia the IAID * * @return the binding (a wrapped IdentityAssoc) */ private Binding buildBinding(DhcpLink clientLink, byte[] duid, byte iatype, long iaid, byte state) { IdentityAssoc ia = new IdentityAssoc(); ia.setDuid(duid); ia.setIatype(iatype); ia.setIaid(iaid); ia.setState(state); return new Binding(ia, clientLink); } /** * Builds the set of binding objects for the client request. * * @param clientLink the client link * @param inetAddrs the list of IP addresses for the client binding * @param requestMsg the request message * @param state the binding state * * @return the set<binding object> */ private Set<BindingObject> buildBindingObjects(DhcpLink clientLink, List<InetAddress> inetAddrs, DhcpMessage requestMsg, byte state) { Set<BindingObject> bindingObjs = new HashSet<BindingObject>(); for (InetAddress inetAddr : inetAddrs) { if (log.isDebugEnabled()) log.debug("Building BindingObject for IP=" + inetAddr.getHostAddress()); BindingObject bindingObj = buildBindingObject(inetAddr, clientLink, requestMsg); if (bindingObj != null) { bindingObj.setState(state); bindingObjs.add(bindingObj); } else { log.warn("Failed to build BindingObject for IP=" + inetAddr.getHostAddress()); } } return bindingObjs; } /** * Build the appropriate type of BindingObject. This method is implemented by the * subclasses to create an NA/TA BindingAddress object or a BindingPrefix object * or a V4AddressBinding object. * * @param inetAddr the IP address for this binding object * @param clientLink the client link * @param requestMsg the request message * @return the binding object */ protected abstract BindingObject buildBindingObject(InetAddress inetAddr, DhcpLink clientLink, DhcpMessage requestMsg); /** * Build a BindingObject from a static binding. Because the StaticBinding * implements the DhcpConfigObject interface, it can be used directly in * creating the BindingObject, unlike buildBindingObject above which is * abstract because each implementation must lookup the object in that * binding manager's map of binding pools. * * @param inetAddr the IP address for this binding object * @param staticBinding the static binding * @return the binding object */ protected BindingObject buildStaticBindingObject(InetAddress inetAddr, StaticBinding staticBinding) { IaAddress iaAddr = new IaAddress(); iaAddr.setIpAddress(inetAddr); V6BindingAddress bindingAddr = new V6BindingAddress(iaAddr, staticBinding); setBindingObjectTimes(bindingAddr, staticBinding.getPreferredLifetimeMs(), staticBinding.getPreferredLifetimeMs()); return bindingAddr; } /** * Sets the lifetimes of the given collection of binding objects. * * @param bindingObjs the collection of binding objects to set lifetimes in */ protected void setBindingObjsTimes(Collection<BindingObject> bindingObjs) { if ((bindingObjs != null) && !bindingObjs.isEmpty()) { for (BindingObject bindingObj : bindingObjs) { DhcpConfigObject configObj = bindingObj.getConfigObj(); setBindingObjectTimes(bindingObj, configObj.getPreferredLifetimeMs(), configObj.getValidLifetimeMs()); //TODO: if we store the options, and they have changed, // then we must update those options here somehow } } } /** * Sets the lifetimes of the given binding object. * * @param bindingObj the binding object * @param preferred the preferred lifetime in ms * @param valid the valid lifetime in ms */ protected void setBindingObjectTimes(BindingObject bindingObj, long preferred, long valid) { if (log.isDebugEnabled()) log.debug("Updating binding times for address: " + bindingObj.getIpAddress().getHostAddress() + " preferred=" + preferred + ", valid=" + valid); Date now = new Date(); bindingObj.setStartTime(now); if (preferred < 0) { // infinite lease bindingObj.setPreferredEndTime(new Date(-1)); } else { long pEndTime = now.getTime() + preferred; bindingObj.setPreferredEndTime(new Date(pEndTime)); } if (valid < 0) { // infinite lease bindingObj.setValidEndTime(new Date(-1)); } else { long vEndTime = now.getTime() + valid; bindingObj.setValidEndTime(new Date(vEndTime)); } } public IaManager getIaMgr() { return iaMgr; } public void setIaMgr(IaManager iaMgr) { this.iaMgr = iaMgr; } }