Skip to content

Commit

Permalink
Make pcap listener work.
Browse files Browse the repository at this point in the history
  • Loading branch information
shevek committed Mar 25, 2015
1 parent c664b62 commit 0e03c5e
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 100 deletions.
5 changes: 5 additions & 0 deletions README.md
Expand Up @@ -9,3 +9,8 @@ overhaul into a modern, correct and complete DHCP implementation.
The [JavaDoc API](http://shevek.github.io/dhcp4j/docs/javadoc/)
is available.

For UDP:
sudo setcap 'cap_net_bind_service=+ep' `readlink -f /usr/bin/java`
For raw:
sudo setcap 'cap_net_bind_service,cap_net_raw+ep' `readlink -f /usr/bin/java`

Expand Up @@ -42,16 +42,14 @@ public Lease getLease(@Nonnull HardwareAddress hardwareAddress) {
}

@Nonnull
public Lease addLease(@Nonnull HardwareAddress hardwareAddress, @Nonnull Lease lease) {
leases.put(hardwareAddress, lease);
public Lease addLease(@Nonnull Lease lease) {
leases.put(lease.getHardwareAddress(), lease);
return lease;
}

@Nonnull
public Lease addLease(@Nonnull HardwareAddress hardwareAddress, @Nonnull InetAddress clientAddress) {
Lease lease = new Lease();
lease.setClientAddress(clientAddress);
return addLease(hardwareAddress, lease);
return addLease(new Lease(hardwareAddress, clientAddress));
}

@Nonnull
Expand Down
Expand Up @@ -19,7 +19,9 @@
*/
package org.apache.directory.server.dhcp.service.store;

import com.google.common.base.Preconditions;
import java.net.InetAddress;
import javax.annotation.Nonnull;
import org.apache.directory.server.dhcp.messages.HardwareAddress;
import org.apache.directory.server.dhcp.options.OptionsField;

Expand All @@ -43,41 +45,40 @@ public static enum LeaseState {
/** Lease state: expired */
EXPIRED;
}
private LeaseState state;

/**
* The assigned client address.
* The client's hardware address.
*/
private InetAddress clientAddress;

private final HardwareAddress hardwareAddress;
/**
* The client's hardware address.
* The assigned client address.
*/
private HardwareAddress hardwareAddress;

private final InetAddress clientAddress;
private LeaseState state = LeaseState.NEW;
/**
* The next-server (boot-server) address.
*/
private InetAddress nextServerAddress;

/**
* The DhcpOptions to provide to the client along with the lease.
*/
private OptionsField options = new OptionsField();

private final OptionsField options = new OptionsField();
private long acquired = -1;

private long expires = -1;

/**
* @return InetAddress
*/
public InetAddress getClientAddress() {
return clientAddress;
public Lease(@Nonnull HardwareAddress hardwareAddress, @Nonnull InetAddress clientAddress) {
this.hardwareAddress = Preconditions.checkNotNull(hardwareAddress, "Hardware address was null.");
this.clientAddress = Preconditions.checkNotNull(clientAddress, "Client address was null.");
}

public void setClientAddress(InetAddress clientAddress) {
this.clientAddress = clientAddress;
@Nonnull
public HardwareAddress getHardwareAddress() {
return hardwareAddress;
}

@Nonnull
public InetAddress getClientAddress() {
return clientAddress;
}

/**
Expand Down Expand Up @@ -112,26 +113,22 @@ public void setState(LeaseState state) {
this.state = state;
}

public HardwareAddress getHardwareAddress() {
return hardwareAddress;
}

public void setHardwareAddress(HardwareAddress hardwareAddress) {
this.hardwareAddress = hardwareAddress;
}

/** Returns the time in seconds since the epoch at which this lease was acquired. */
public long getAcquired() {
return acquired;
}

/** Sets the time in seconds since the epoch at which this lease was acquired. */
public void setAcquired(long acquired) {
this.acquired = acquired;
}

/** Returns the time in seconds since the epoch at which this lease expires. */
public long getExpires() {
return expires;
}

/** Sets the time in seconds since the epoch at which this lease expires. */
public void setExpires(long expires) {
this.expires = expires;
}
Expand Down
Expand Up @@ -85,8 +85,10 @@ public DhcpMessage leaseOffer(
throws DhcpException {
HardwareAddress hardwareAddress = request.getHardwareAddress();
Lease lease = leases.getIfPresent(hardwareAddress);
if (lease != null)
if (lease != null) {
lease.setState(Lease.LeaseState.OFFERED);
return newReply(request, MessageType.DHCPOFFER, lease);
}

DhcpConfigSubnet subnet = findSubnet(context);
if (subnet == null)
Expand All @@ -95,10 +97,8 @@ public DhcpMessage leaseOffer(
long leaseTimeSecs = getLeaseTime(TTL_OFFER, clientRequestedExpirySecs);

// TODO: Allocate a new address.
lease = new Lease();
lease.setHardwareAddress(hardwareAddress);
lease = new Lease(hardwareAddress, clientRequestedAddress);
lease.setState(Lease.LeaseState.OFFERED);
lease.setClientAddress(clientRequestedAddress);
lease.setExpires(System.currentTimeMillis() / 1000 + leaseTimeSecs);
lease.getOptions().setAddressOption(SubnetMask.class, subnet.getNetwork().getNetmaskAddress());
leases.put(hardwareAddress, lease);
Expand Down Expand Up @@ -130,16 +130,18 @@ public boolean leaseDecline(
DhcpRequestContext context,
DhcpMessage request,
InetAddress clientAddress) throws DhcpException {
leases.invalidate(request.getHardwareAddress());
return true; // Should check if present.
Lease lease = leases.asMap().remove(request.getHardwareAddress());
return lease != null
&& Objects.equal(lease.getClientAddress(), clientAddress);
}

@Override
public boolean leaseRelease(
DhcpRequestContext context,
DhcpMessage request,
InetAddress clientAddress) throws DhcpException {
leases.invalidate(request.getHardwareAddress());
return true; // Should check if present.
Lease lease = leases.asMap().remove(request.getHardwareAddress());
return lease != null
&& Objects.equal(lease.getClientAddress(), clientAddress);
}
}
Expand Up @@ -25,8 +25,8 @@
import org.anarres.dhcp.common.MDCUtils;
import org.anarres.dhcp.common.address.InterfaceAddress;
import org.apache.directory.server.dhcp.io.DhcpInterfaceManager;
import org.apache.directory.server.dhcp.io.DhcpRequestContext;
import org.apache.directory.server.dhcp.io.DhcpInterfaceUtils;
import org.apache.directory.server.dhcp.io.DhcpRequestContext;
import org.apache.directory.server.dhcp.messages.DhcpMessage;
import org.apache.directory.server.dhcp.service.DhcpService;
import org.apache.mina.core.service.IoHandlerAdapter;
Expand All @@ -35,7 +35,6 @@
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

/**
* Implementation of a DHCP protocol handler which delegates the work of
Expand Down
@@ -0,0 +1,91 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.anarres.dhcp.server.pcap;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import javax.annotation.Nonnull;
import org.anarres.dhcp.common.address.InterfaceAddress;
import org.apache.directory.server.dhcp.io.DhcpMessageDecoder;
import org.apache.directory.server.dhcp.io.DhcpMessageEncoder;
import org.apache.directory.server.dhcp.io.DhcpRequestContext;
import org.apache.directory.server.dhcp.messages.DhcpMessage;
import org.apache.directory.server.dhcp.service.DhcpService;
import org.pcap4j.core.PacketListener;
import org.pcap4j.packet.EthernetPacket;
import org.pcap4j.packet.IpV4Packet;
import org.pcap4j.packet.Packet;
import org.pcap4j.packet.UdpPacket;
import org.pcap4j.packet.UnknownPacket;
import org.pcap4j.packet.namednumber.EtherType;
import org.pcap4j.packet.namednumber.IpNumber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
*
* @author shevek
*/
public class DhcpPacketListener implements PacketListener {

private static final Logger LOG = LoggerFactory.getLogger(DhcpPacketListener.class);
private final DhcpMessageDecoder decoder = new DhcpMessageDecoder();
private final DhcpMessageEncoder encoder = new DhcpMessageEncoder();
private final DhcpService service;
private final InterfaceAddress[] interfaceAddresses;

public DhcpPacketListener(@Nonnull DhcpService service, @Nonnull InterfaceAddress[] interfaceAddresses) {
this.service = service;
this.interfaceAddresses = interfaceAddresses;
}

public void gotPacket(Packet rawPacket) {
try {
LOG.info("Read raw " + rawPacket);
IpV4Packet ipPacket = rawPacket.get(IpV4Packet.class);
UdpPacket udpPacket = rawPacket.get(UdpPacket.class);
byte[] dhcpData = udpPacket.getPayload().getRawData();
InetSocketAddress remoteAddress = new InetSocketAddress(ipPacket.getHeader().getSrcAddr(), udpPacket.getHeader().getSrcPort().valueAsInt());
InetSocketAddress localAddress = new InetSocketAddress(ipPacket.getHeader().getDstAddr(), udpPacket.getHeader().getDstPort().valueAsInt());
DhcpRequestContext context = new DhcpRequestContext(interfaceAddresses, remoteAddress, localAddress);
DhcpMessage request = decoder.decode(ByteBuffer.wrap(dhcpData));
LOG.info("Read DHCP " + request);
DhcpMessage reply = service.getReplyFor(context, request);
if (reply == null)
return;
byte[] replyData = new byte[1536];
ByteBuffer buffer = ByteBuffer.wrap(replyData);
encoder.encode(buffer, reply);
replyData = Arrays.copyOf(replyData, buffer.position()); // Truncate array to writer position.
UnknownPacket.Builder dhcpBuilder = new UnknownPacket.Builder()
.rawData(replyData);
UdpPacket.Builder udpBuilder = new UdpPacket.Builder()
.payloadBuilder(dhcpBuilder)
.srcPort(udpPacket.getHeader().getDstPort())
.dstPort(udpPacket.getHeader().getSrcPort())
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true);
IpV4Packet.Builder ipBuilder = new IpV4Packet.Builder()
.payloadBuilder(udpBuilder)
.srcAddr(null) // TODO
.dstAddr(null) // TODO
.protocol(IpNumber.UDP)
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true)
.paddingAtBuild(true);
EthernetPacket.Builder ethernetBuilder = new EthernetPacket.Builder()
.payloadBuilder(ipBuilder)
.type(EtherType.IPV4)
.paddingAtBuild(true);
Packet replyPacket = ethernetBuilder.build();
// handle.sendPacket(replyPacket);
} catch (Exception e) {
LOG.error("DHCP failed", e);
}
}

}

0 comments on commit 0e03c5e

Please sign in to comment.