001/*
002 * To change this license header, choose License Headers in Project Properties.
003 * To change this template file, choose Tools | Templates
004 * and open the template in the editor.
005 */
006package org.anarres.dhcp.server.pcap;
007
008import java.net.InetSocketAddress;
009import java.nio.ByteBuffer;
010import java.util.Arrays;
011import javax.annotation.Nonnull;
012import org.anarres.dhcp.common.address.InterfaceAddress;
013import org.apache.directory.server.dhcp.io.DhcpMessageDecoder;
014import org.apache.directory.server.dhcp.io.DhcpMessageEncoder;
015import org.apache.directory.server.dhcp.io.DhcpRequestContext;
016import org.apache.directory.server.dhcp.messages.DhcpMessage;
017import org.apache.directory.server.dhcp.service.DhcpService;
018import org.pcap4j.core.PacketListener;
019import org.pcap4j.packet.EthernetPacket;
020import org.pcap4j.packet.IpV4Packet;
021import org.pcap4j.packet.Packet;
022import org.pcap4j.packet.UdpPacket;
023import org.pcap4j.packet.UnknownPacket;
024import org.pcap4j.packet.namednumber.EtherType;
025import org.pcap4j.packet.namednumber.IpNumber;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029/**
030 *
031 * @author shevek
032 */
033public class DhcpPacketListener implements PacketListener {
034
035    private static final Logger LOG = LoggerFactory.getLogger(DhcpPacketListener.class);
036    private final DhcpMessageDecoder decoder = new DhcpMessageDecoder();
037    private final DhcpMessageEncoder encoder = new DhcpMessageEncoder();
038    private final DhcpService service;
039    private final InterfaceAddress[] interfaceAddresses;
040
041    public DhcpPacketListener(@Nonnull DhcpService service, @Nonnull InterfaceAddress[] interfaceAddresses) {
042        this.service = service;
043        this.interfaceAddresses = interfaceAddresses;
044    }
045
046    public void gotPacket(Packet rawPacket) {
047        try {
048            LOG.info("Read raw " + rawPacket);
049            IpV4Packet ipPacket = rawPacket.get(IpV4Packet.class);
050            UdpPacket udpPacket = rawPacket.get(UdpPacket.class);
051            byte[] dhcpData = udpPacket.getPayload().getRawData();
052            InetSocketAddress remoteAddress = new InetSocketAddress(ipPacket.getHeader().getSrcAddr(), udpPacket.getHeader().getSrcPort().valueAsInt());
053            InetSocketAddress localAddress = new InetSocketAddress(ipPacket.getHeader().getDstAddr(), udpPacket.getHeader().getDstPort().valueAsInt());
054            DhcpRequestContext context = new DhcpRequestContext(interfaceAddresses, remoteAddress, localAddress);
055            DhcpMessage request = decoder.decode(ByteBuffer.wrap(dhcpData));
056            LOG.info("Read DHCP " + request);
057            DhcpMessage reply = service.getReplyFor(context, request);
058            if (reply == null)
059                return;
060            byte[] replyData = new byte[1536];
061            ByteBuffer buffer = ByteBuffer.wrap(replyData);
062            encoder.encode(buffer, reply);
063            replyData = Arrays.copyOf(replyData, buffer.position());    // Truncate array to writer position.
064            UnknownPacket.Builder dhcpBuilder = new UnknownPacket.Builder()
065                    .rawData(replyData);
066            UdpPacket.Builder udpBuilder = new UdpPacket.Builder()
067                    .payloadBuilder(dhcpBuilder)
068                    .srcPort(udpPacket.getHeader().getDstPort())
069                    .dstPort(udpPacket.getHeader().getSrcPort())
070                    .correctChecksumAtBuild(true)
071                    .correctLengthAtBuild(true);
072            IpV4Packet.Builder ipBuilder = new IpV4Packet.Builder()
073                    .payloadBuilder(udpBuilder)
074                    .srcAddr(null) // TODO
075                    .dstAddr(null) // TODO
076                    .protocol(IpNumber.UDP)
077                    .correctChecksumAtBuild(true)
078                    .correctLengthAtBuild(true)
079                    .paddingAtBuild(true);
080            EthernetPacket.Builder ethernetBuilder = new EthernetPacket.Builder()
081                    .payloadBuilder(ipBuilder)
082                    .type(EtherType.IPV4)
083                    .paddingAtBuild(true);
084            Packet replyPacket = ethernetBuilder.build();
085            // handle.sendPacket(replyPacket);
086        } catch (Exception e) {
087            LOG.error("DHCP failed", e);
088        }
089    }
090
091}