/* * Copyright 2011 Future Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.dhcp.server; import java.io.IOException; import java.net.InetAddress; import java.sql.SQLException; import java.util.Date; import java.util.List; import org.krakenapps.dhcp.DhcpMessage; import org.krakenapps.dhcp.DhcpOption; import org.krakenapps.dhcp.DhcpOptionCode; import org.krakenapps.dhcp.DhcpServer; import org.krakenapps.dhcp.model.DhcpIpGroup; import org.krakenapps.dhcp.model.DhcpIpLease; import org.krakenapps.dhcp.model.DhcpIpReservation; import org.krakenapps.dhcp.model.DhcpOptionConfig; import org.krakenapps.dhcp.options.ByteConverter; import org.krakenapps.dhcp.options.RequestedIpAddressOption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class DhcpRequestHandler { private final Logger logger = LoggerFactory.getLogger(DhcpRequestHandler.class.getName()); private DhcpServer server; private DhcpServerContext c; public DhcpRequestHandler(DhcpServer server, DhcpServerContext c) { this.server = server; this.c = c; } public void handle(DhcpMessage msg) throws IOException { // check if blocked if (c.blockFilters.containsKey(msg.getClientMac())) { logger.info("kraken dhcp: client [{}] discover ignored by block list", msg.getClientMac()); return; } // request ip InetAddress requestedIp = getRequestedIp(msg); if (requestedIp == null) { logger.trace("kraken dhcp: invalid dhcp request, null request ip from {}", msg.getClientMac()); return; } DhcpIpGroup group = server.getIpGroup(requestedIp); if (group == null) { logger.trace("kraken dhcp: ip group not found for {}", requestedIp.getHostAddress()); return; } List<DhcpOptionConfig> configs = server.getGroupOptions(group.getName()); InetAddress serverIp = DhcpDatabase.getServerIdentifier(configs); // check server identifier, and ignore if not matched DhcpOption serverOption = msg.getOption(DhcpOptionCode.ServerIdentifier.code()); if (serverOption != null) { InetAddress ip = ByteConverter.toInetAddress(serverOption.getValue()); if (!ip.equals(serverIp)) { logger.info("kraken dhcp: ignored request to other dhcp server [{}]", ip.getHostAddress()); return; } } // check ip reservation DhcpIpReservation r = c.reserveMap.get(msg.getClientMac()); if (r != null && !r.getIp().equals(requestedIp)) { // reject and induce dhcp discover, then device will use // reserved ip c.send(DhcpMessageBuilder.newNak(msg, serverIp)); return; } // check if already used DhcpIpLease lease = c.leaseMap.get(requestedIp); if (lease != null) { handleWithOldLease(msg, requestedIp, lease); return; } // check if offered to this client DhcpIpLease offer = c.offerMap.get(requestedIp); if (offer != null && !offer.getMac().equals(msg.getClientMac())) { handleIpConflict(msg, requestedIp, offer, serverIp); return; } lease(group, configs, msg, requestedIp, offer); } private InetAddress getRequestedIp(DhcpMessage msg) { InetAddress requestedIp = null; RequestedIpAddressOption requestedIpOption = (RequestedIpAddressOption) msg .getOption(DhcpOptionCode.RequestedIpAddress.code()); if (requestedIpOption == null) { if (!msg.getClientAddress().equals(DhcpServerContext.ZERO_ADDRESS)) requestedIp = msg.getClientAddress(); } else { requestedIp = requestedIpOption.getRequestedIp(); } return requestedIp; } private void lease(DhcpIpGroup group, List<DhcpOptionConfig> configs, DhcpMessage msg, InetAddress requestedIp, DhcpIpLease offer) throws IOException { String hostName = DhcpOptions.getHostName(msg); InetAddress serverIp = DhcpDatabase.getServerIdentifier(configs); int leaseDuration = DhcpDatabase.getLeaseDuration(configs); // no previous offer found if (offer == null) offer = new DhcpIpLease(group.getName(), requestedIp, msg.getClientMac(), hostName, leaseDuration); DhcpIpLease lease = offer; try { // Success! DhcpDatabase.createIpLease(lease); c.leaseMap.put(requestedIp, lease); DhcpMessage m = DhcpMessageBuilder.newAck(msg, configs, requestedIp); logger.info("kraken dhcp: sending ack [{}]", m); c.send(m); } catch (RuntimeException e) { // duplicated entry if (e.getCause() instanceof SQLException) { DhcpMessage m = DhcpMessageBuilder.newNak(msg, serverIp); logger.info("kraken dhcp: ip conflict, sending nack [{}]", m); c.send(m); } } } private void handleIpConflict(DhcpMessage msg, InetAddress requestedIp, DhcpIpLease offer, InetAddress serverIp) throws IOException { logger.trace("kraken dhcp: request ip [{}] from [{}] is already offered to other client [{}]", new Object[] { requestedIp, msg.getClientMac(), offer.getMac() }); DhcpMessage m = DhcpMessageBuilder.newNak(msg, serverIp); logger.trace("kraken dhcp: already offered to others, sending nack [{}]", m); c.send(m); } private void handleWithOldLease(DhcpMessage msg, InetAddress requestedIp, DhcpIpLease lease) throws IOException { DhcpIpGroup group = server.getIpGroup(requestedIp); List<DhcpOptionConfig> configs = server.getGroupOptions(group.getName()); int leaseDuration = DhcpDatabase.getLeaseDuration(configs); InetAddress serverIp = DhcpDatabase.getServerIdentifier(configs); if (lease.getExpire().before(new Date())) { // replace expired lease lease.setMac(msg.getClientMac()); lease.setHostName(DhcpOptions.getHostName(msg)); lease.setCreated(new Date()); lease.setUpdated(new Date()); lease.setNewExpire(leaseDuration); DhcpDatabase.updateIpLease(c.conn, lease); c.send(DhcpMessageBuilder.newAck(msg, configs, requestedIp)); logger.trace("kraken dhcp: replaced expired ip [{}] with [{}]", requestedIp.getHostAddress(), msg .getClientMac()); } else if (lease.getMac().equals(msg.getClientMac())) { // extend expire lease.setUpdated(new Date()); lease.setNewExpire(leaseDuration); DhcpDatabase.updateIpLease(c.conn, lease); c.send(DhcpMessageBuilder.newAck(msg, configs, requestedIp)); logger.trace("kraken dhcp: extend ip lease [{}] from owner [{}]", requestedIp, msg.getClientMac()); } else { logger.trace("kraken dhcp: request ip [{}] from [{}] is already leased by other client [{}]", new Object[] { requestedIp, msg.getClientMac(), lease.getMac() }); c.send(DhcpMessageBuilder.newNak(msg, serverIp)); } } }