package android.net.dhcp; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; /** * Defines basic data and operations needed to build and use packets for the * DHCP protocol. Subclasses create the specific packets used at each * stage of the negotiation. */ abstract class DhcpPacket { protected static final String TAG = "DhcpPacket"; /** * Packet encapsulations. */ public static final int ENCAP_L2 = 0; // EthernetII header included public static final int ENCAP_L3 = 1; // IP/UDP header included public static final int ENCAP_BOOTP = 2; // BOOTP contents only /** * IP layer definitions. */ private static final byte IP_TYPE_UDP = (byte) 0x11; /** * IP: Version 4, Header Length 20 bytes */ private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45; /** * IP: Flags 0, Fragment Offset 0, Don't Fragment */ private static final short IP_FLAGS_OFFSET = (short) 0x4000; /** * IP: TOS */ private static final byte IP_TOS_LOWDELAY = (byte) 0x10; /** * IP: TTL -- use default 64 from RFC1340 */ private static final byte IP_TTL = (byte) 0x40; /** * The client DHCP port. */ static final short DHCP_CLIENT = (short) 68; /** * The server DHCP port. */ static final short DHCP_SERVER = (short) 67; /** * The message op code indicating a request from a client. */ protected static final byte DHCP_BOOTREQUEST = (byte) 1; /** * The message op code indicating a response from the server. */ protected static final byte DHCP_BOOTREPLY = (byte) 2; /** * The code type used to identify an Ethernet MAC address in the * Client-ID field. */ protected static final byte CLIENT_ID_ETHER = (byte) 1; /** * The maximum length of a packet that can be constructed. */ protected static final int MAX_LENGTH = 1500; /** * DHCP Optional Type: DHCP Subnet Mask */ protected static final byte DHCP_SUBNET_MASK = 1; protected InetAddress mSubnetMask; /** * DHCP Optional Type: DHCP Router */ protected static final byte DHCP_ROUTER = 3; protected InetAddress mGateway; /** * DHCP Optional Type: DHCP DNS Server */ protected static final byte DHCP_DNS_SERVER = 6; protected List mDnsServers; /** * DHCP Optional Type: DHCP Host Name */ protected static final byte DHCP_HOST_NAME = 12; protected String mHostName; /** * DHCP Optional Type: DHCP DOMAIN NAME */ protected static final byte DHCP_DOMAIN_NAME = 15; protected String mDomainName; /** * DHCP Optional Type: DHCP BROADCAST ADDRESS */ protected static final byte DHCP_BROADCAST_ADDRESS = 28; protected InetAddress mBroadcastAddress; /** * DHCP Optional Type: DHCP Requested IP Address */ protected static final byte DHCP_REQUESTED_IP = 50; protected InetAddress mRequestedIp; /** * DHCP Optional Type: DHCP Lease Time */ protected static final byte DHCP_LEASE_TIME = 51; protected Integer mLeaseTime; /** * DHCP Optional Type: DHCP Message Type */ protected static final byte DHCP_MESSAGE_TYPE = 53; // the actual type values protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1; protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2; protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3; protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4; protected static final byte DHCP_MESSAGE_TYPE_ACK = 5; protected static final byte DHCP_MESSAGE_TYPE_NAK = 6; protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8; /** * DHCP Optional Type: DHCP Server Identifier */ protected static final byte DHCP_SERVER_IDENTIFIER = 54; protected InetAddress mServerIdentifier; /** * DHCP Optional Type: DHCP Parameter List */ protected static final byte DHCP_PARAMETER_LIST = 55; protected byte[] mRequestedParams; /** * DHCP Optional Type: DHCP MESSAGE */ protected static final byte DHCP_MESSAGE = 56; protected String mMessage; /** * DHCP Optional Type: DHCP Renewal Time Value */ protected static final byte DHCP_RENEWAL_TIME = 58; /** * DHCP Optional Type: Vendor Class Identifier */ protected static final byte DHCP_VENDOR_CLASS_ID = 60; /** * DHCP Optional Type: DHCP Client Identifier */ protected static final byte DHCP_CLIENT_IDENTIFIER = 61; /** * The transaction identifier used in this particular DHCP negotiation */ protected final int mTransId; /** * The IP address of the client host. This address is typically * proposed by the client (from an earlier DHCP negotiation) or * supplied by the server. */ protected final InetAddress mClientIp; protected final InetAddress mYourIp; private final InetAddress mNextIp; private final InetAddress mRelayIp; /** * Does the client request a broadcast response? */ protected boolean mBroadcast; /** * The six-octet MAC of the client. */ protected final byte[] mClientMac; /** * Asks the packet object to signal the next operation in the DHCP * protocol. The available actions are methods defined in the * DhcpStateMachine interface. */ public abstract void doNextOp(DhcpStateMachine stateMachine); /** * Asks the packet object to create a ByteBuffer serialization of * the packet for transmission. */ public abstract ByteBuffer buildPacket(int encap, short destUdp, short srcUdp); /** * Allows the concrete class to fill in packet-type-specific details, * typically optional parameters at the end of the packet. */ abstract void finishPacket(ByteBuffer buffer); protected DhcpPacket(int transId, InetAddress clientIp, InetAddress yourIp, InetAddress nextIp, InetAddress relayIp, byte[] clientMac, boolean broadcast) { mTransId = transId; mClientIp = clientIp; mYourIp = yourIp; mNextIp = nextIp; mRelayIp = relayIp; mClientMac = clientMac; mBroadcast = broadcast; } /** * Returns the transaction ID. */ public int getTransactionId() { return mTransId; } /** * Creates a new L3 packet (including IP header) containing the * DHCP udp packet. This method relies upon the delegated method * finishPacket() to insert the per-packet contents. */ protected void fillInPacket(int encap, InetAddress destIp, InetAddress srcIp, short destUdp, short srcUdp, ByteBuffer buf, byte requestCode, boolean broadcast) { byte[] destIpArray = destIp.getAddress(); byte[] srcIpArray = srcIp.getAddress(); int ipLengthOffset = 0; int ipChecksumOffset = 0; int endIpHeader = 0; int udpHeaderOffset = 0; int udpLengthOffset = 0; int udpChecksumOffset = 0; buf.clear(); buf.order(ByteOrder.BIG_ENDIAN); // if a full IP packet needs to be generated, put the IP & UDP // headers in place, and pre-populate with artificial values // needed to seed the IP checksum. if (encap == ENCAP_L3) { // fake IP header, used in the IP-header checksum buf.put(IP_VERSION_HEADER_LEN); buf.put(IP_TOS_LOWDELAY); // tos: IPTOS_LOWDELAY ipLengthOffset = buf.position(); buf.putShort((short)0); // length buf.putShort((short)0); // id buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment buf.put(IP_TTL); // TTL: use default 64 from RFC1340 buf.put(IP_TYPE_UDP); ipChecksumOffset = buf.position(); buf.putShort((short) 0); // checksum buf.put(srcIpArray); buf.put(destIpArray); endIpHeader = buf.position(); // UDP header udpHeaderOffset = buf.position(); buf.putShort(srcUdp); buf.putShort(destUdp); udpLengthOffset = buf.position(); buf.putShort((short) 0); // length udpChecksumOffset = buf.position(); buf.putShort((short) 0); // UDP checksum -- initially zero } // DHCP payload buf.put(requestCode); buf.put((byte) 1); // Hardware Type: Ethernet buf.put((byte) mClientMac.length); // Hardware Address Length buf.put((byte) 0); // Hop Count buf.putInt(mTransId); // Transaction ID buf.putShort((short) 0); // Elapsed Seconds if (broadcast) { buf.putShort((short) 0x8000); // Flags } else { buf.putShort((short) 0x0000); // Flags } buf.put(mClientIp.getAddress()); buf.put(mYourIp.getAddress()); buf.put(mNextIp.getAddress()); buf.put(mRelayIp.getAddress()); buf.put(mClientMac); buf.position(buf.position() + (16 - mClientMac.length) // pad addr to 16 bytes + 64 // empty server host name (64 bytes) + 128); // empty boot file name (128 bytes) buf.putInt(0x63825363); // magic number finishPacket(buf); // round up to an even number of octets if ((buf.position() & 1) == 1) { buf.put((byte) 0); } // If an IP packet is being built, the IP & UDP checksums must be // computed. if (encap == ENCAP_L3) { // fix UDP header: insert length short udpLen = (short)(buf.position() - udpHeaderOffset); buf.putShort(udpLengthOffset, udpLen); // fix UDP header: checksum // checksum for UDP at udpChecksumOffset int udpSeed = 0; // apply IPv4 pseudo-header. Read IP address src and destination // values from the IP header and accumulate checksum. udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2)); udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4)); udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6)); udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8)); // accumulate extra data for the pseudo-header udpSeed += IP_TYPE_UDP; udpSeed += udpLen; // and compute UDP checksum buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed, udpHeaderOffset, buf.position())); // fix IP header: insert length buf.putShort(ipLengthOffset, (short)buf.position()); // fixup IP-header checksum buf.putShort(ipChecksumOffset, (short) checksum(buf, 0, 0, endIpHeader)); } } /** * Converts a signed short value to an unsigned int value. Needed * because Java does not have unsigned types. */ private int intAbs(short v) { if (v < 0) { int r = v + 65536; return r; } else { return(v); } } /** * Performs an IP checksum (used in IP header and across UDP * payload) on the specified portion of a ByteBuffer. The seed * allows the checksum to commence with a specified value. */ private int checksum(ByteBuffer buf, int seed, int start, int end) { int sum = seed; int bufPosition = buf.position(); // set position of original ByteBuffer, so that the ShortBuffer // will be correctly initialized buf.position(start); ShortBuffer shortBuf = buf.asShortBuffer(); // re-set ByteBuffer position buf.position(bufPosition); short[] shortArray = new short[(end - start) / 2]; shortBuf.get(shortArray); for (short s : shortArray) { sum += intAbs(s); } start += shortArray.length * 2; // see if a singleton byte remains if (end != start) { short b = buf.get(start); // make it unsigned if (b < 0) { b += 256; } sum += b * 256; } sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF); sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF); int negated = ~sum; return intAbs((short) negated); } /** * Adds an optional parameter containing a single byte value. */ protected void addTlv(ByteBuffer buf, byte type, byte value) { buf.put(type); buf.put((byte) 1); buf.put(value); } /** * Adds an optional parameter containing an array of bytes. */ protected void addTlv(ByteBuffer buf, byte type, byte[] payload) { if (payload != null) { buf.put(type); buf.put((byte) payload.length); buf.put(payload); } } /** * Adds an optional parameter containing an IP address. */ protected void addTlv(ByteBuffer buf, byte type, InetAddress addr) { if (addr != null) { addTlv(buf, type, addr.getAddress()); } } /** * Adds an optional parameter containing a list of IP addresses. */ protected void addTlv(ByteBuffer buf, byte type, List addrs) { if (addrs != null && addrs.size() > 0) { buf.put(type); buf.put((byte)(4 * addrs.size())); for (InetAddress addr : addrs) { buf.put(addr.getAddress()); } } } /** * Adds an optional parameter containing a simple integer */ protected void addTlv(ByteBuffer buf, byte type, Integer value) { if (value != null) { buf.put(type); buf.put((byte) 4); buf.putInt(value.intValue()); } } /** * Adds an optional parameter containing and ASCII string. */ protected void addTlv(ByteBuffer buf, byte type, String str) { if (str != null) { buf.put(type); buf.put((byte) str.length()); for (int i = 0; i < str.length(); i++) { buf.put((byte) str.charAt(i)); } } } /** * Adds the special end-of-optional-parameters indicator. */ protected void addTlvEnd(ByteBuffer buf) { buf.put((byte) 0xFF); } /** * Converts a MAC from an array of octets to an ASCII string. */ public static String macToString(byte[] mac) { String macAddr = ""; for (int i = 0; i < mac.length; i++) { String hexString = "0" + Integer.toHexString(mac[i]); // substring operation grabs the last 2 digits: this // allows signed bytes to be converted correctly. macAddr += hexString.substring(hexString.length() - 2); if (i != (mac.length - 1)) { macAddr += ":"; } } return macAddr; } public String toString() { String macAddr = macToString(mClientMac); return macAddr; } /** * Reads a four-octet value from a ByteBuffer and construct * an IPv4 address from that value. */ private static InetAddress readIpAddress(ByteBuffer packet) { InetAddress result = null; byte[] ipAddr = new byte[4]; packet.get(ipAddr); try { result = InetAddress.getByAddress(ipAddr); } catch (UnknownHostException ex) { // ipAddr is numeric, so this should not be // triggered. However, if it is, just nullify result = null; } return result; } /** * Reads a string of specified length from the buffer. */ private static String readAsciiString(ByteBuffer buf, int byteCount) { byte[] bytes = new byte[byteCount]; buf.get(bytes); return new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII); } /** * Creates a concrete DhcpPacket from the supplied ByteBuffer. The * buffer may have an L2 encapsulation (which is the full EthernetII * format starting with the source-address MAC) or an L3 encapsulation * (which starts with the IP header). *
* A subset of the optional parameters are parsed and are stored * in object fields. */ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) { // bootp parameters int transactionId; InetAddress clientIp; InetAddress yourIp; InetAddress nextIp; InetAddress relayIp; byte[] clientMac; List dnsServers = new ArrayList(); InetAddress gateway = null; // aka router Integer leaseTime = null; InetAddress serverIdentifier = null; InetAddress netMask = null; String message = null; String vendorId = null; byte[] expectedParams = null; String hostName = null; String domainName = null; InetAddress ipSrc = null; InetAddress ipDst = null; InetAddress bcAddr = null; InetAddress requestedIp = null; // dhcp options byte dhcpType = (byte) 0xFF; packet.order(ByteOrder.BIG_ENDIAN); // check to see if we need to parse L2, IP, and UDP encaps if (pktType == ENCAP_L2) { // System.out.println("buffer len " + packet.limit()); byte[] l2dst = new byte[6]; byte[] l2src = new byte[6]; packet.get(l2dst); packet.get(l2src); short l2type = packet.getShort(); if (l2type != 0x0800) return null; } if ((pktType == ENCAP_L2) || (pktType == ENCAP_L3)) { // assume l2type is 0x0800, i.e. IP byte ipType = packet.get(); // System.out.println("ipType is " + ipType); byte ipDiffServicesField = packet.get(); short ipTotalLength = packet.getShort(); short ipIdentification = packet.getShort(); byte ipFlags = packet.get(); byte ipFragOffset = packet.get(); byte ipTTL = packet.get(); byte ipProto = packet.get(); short ipChksm = packet.getShort(); ipSrc = readIpAddress(packet); ipDst = readIpAddress(packet); if (ipProto != IP_TYPE_UDP) // UDP return null; // assume UDP short udpSrcPort = packet.getShort(); short udpDstPort = packet.getShort(); short udpLen = packet.getShort(); short udpChkSum = packet.getShort(); if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT)) return null; } // assume bootp byte type = packet.get(); byte hwType = packet.get(); byte addrLen = packet.get(); byte hops = packet.get(); transactionId = packet.getInt(); short elapsed = packet.getShort(); short bootpFlags = packet.getShort(); boolean broadcast = (bootpFlags & 0x8000) != 0; byte[] ipv4addr = new byte[4]; try { packet.get(ipv4addr); clientIp = InetAddress.getByAddress(ipv4addr); packet.get(ipv4addr); yourIp = InetAddress.getByAddress(ipv4addr); packet.get(ipv4addr); nextIp = InetAddress.getByAddress(ipv4addr); packet.get(ipv4addr); relayIp = InetAddress.getByAddress(ipv4addr); } catch (UnknownHostException ex) { return null; } clientMac = new byte[addrLen]; packet.get(clientMac); // skip over address padding (16 octets allocated) packet.position(packet.position() + (16 - addrLen) + 64 // skip server host name (64 chars) + 128); // skip boot file name (128 chars) int dhcpMagicCookie = packet.getInt(); if (dhcpMagicCookie != 0x63825363) return null; // parse options boolean notFinishedOptions = true; while ((packet.position() < packet.limit()) && notFinishedOptions) { byte optionType = packet.get(); if (optionType == (byte) 0xFF) { notFinishedOptions = false; } else { byte optionLen = packet.get(); int expectedLen = 0; switch(optionType) { case DHCP_SUBNET_MASK: netMask = readIpAddress(packet); expectedLen = 4; break; case DHCP_ROUTER: gateway = readIpAddress(packet); expectedLen = 4; break; case DHCP_DNS_SERVER: expectedLen = 0; for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) { dnsServers.add(readIpAddress(packet)); } break; case DHCP_HOST_NAME: expectedLen = optionLen; hostName = readAsciiString(packet, optionLen); break; case DHCP_DOMAIN_NAME: expectedLen = optionLen; domainName = readAsciiString(packet, optionLen); break; case DHCP_BROADCAST_ADDRESS: bcAddr = readIpAddress(packet); expectedLen = 4; break; case DHCP_REQUESTED_IP: requestedIp = readIpAddress(packet); expectedLen = 4; break; case DHCP_LEASE_TIME: leaseTime = Integer.valueOf(packet.getInt()); expectedLen = 4; break; case DHCP_MESSAGE_TYPE: dhcpType = packet.get(); expectedLen = 1; break; case DHCP_SERVER_IDENTIFIER: serverIdentifier = readIpAddress(packet); expectedLen = 4; break; case DHCP_PARAMETER_LIST: expectedParams = new byte[optionLen]; packet.get(expectedParams); expectedLen = optionLen; break; case DHCP_MESSAGE: expectedLen = optionLen; message = readAsciiString(packet, optionLen); break; case DHCP_VENDOR_CLASS_ID: expectedLen = optionLen; vendorId = readAsciiString(packet, optionLen); break; case DHCP_CLIENT_IDENTIFIER: { // Client identifier byte[] id = new byte[optionLen]; packet.get(id); expectedLen = optionLen; } break; default: // ignore any other parameters for (int i = 0; i < optionLen; i++) { expectedLen++; byte throwaway = packet.get(); } } if (expectedLen != optionLen) { return null; } } } DhcpPacket newPacket; switch(dhcpType) { case -1: return null; case DHCP_MESSAGE_TYPE_DISCOVER: newPacket = new DhcpDiscoverPacket( transactionId, clientMac, broadcast); break; case DHCP_MESSAGE_TYPE_OFFER: newPacket = new DhcpOfferPacket( transactionId, broadcast, ipSrc, yourIp, clientMac); break; case DHCP_MESSAGE_TYPE_REQUEST: newPacket = new DhcpRequestPacket( transactionId, clientIp, clientMac, broadcast); break; case DHCP_MESSAGE_TYPE_DECLINE: newPacket = new DhcpDeclinePacket( transactionId, clientIp, yourIp, nextIp, relayIp, clientMac); break; case DHCP_MESSAGE_TYPE_ACK: newPacket = new DhcpAckPacket( transactionId, broadcast, ipSrc, yourIp, clientMac); break; case DHCP_MESSAGE_TYPE_NAK: newPacket = new DhcpNakPacket( transactionId, clientIp, yourIp, nextIp, relayIp, clientMac); break; case DHCP_MESSAGE_TYPE_INFORM: newPacket = new DhcpInformPacket( transactionId, clientIp, yourIp, nextIp, relayIp, clientMac); break; default: System.out.println("Unimplemented type: " + dhcpType); return null; } newPacket.mBroadcastAddress = bcAddr; newPacket.mDnsServers = dnsServers; newPacket.mDomainName = domainName; newPacket.mGateway = gateway; newPacket.mHostName = hostName; newPacket.mLeaseTime = leaseTime; newPacket.mMessage = message; newPacket.mRequestedIp = requestedIp; newPacket.mRequestedParams = expectedParams; newPacket.mServerIdentifier = serverIdentifier; newPacket.mSubnetMask = netMask; return newPacket; } /** * Parse a packet from an array of bytes. */ public static DhcpPacket decodeFullPacket(byte[] packet, int pktType) { ByteBuffer buffer = ByteBuffer.wrap(packet).order(ByteOrder.BIG_ENDIAN); return decodeFullPacket(buffer, pktType); } /** * Builds a DHCP-DISCOVER packet from the required specified * parameters. */ public static ByteBuffer buildDiscoverPacket(int encap, int transactionId, byte[] clientMac, boolean broadcast, byte[] expectedParams) { DhcpPacket pkt = new DhcpDiscoverPacket( transactionId, clientMac, broadcast); pkt.mRequestedParams = expectedParams; return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); } /** * Builds a DHCP-OFFER packet from the required specified * parameters. */ public static ByteBuffer buildOfferPacket(int encap, int transactionId, boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr, InetAddress gateway, List dnsServers, InetAddress dhcpServerIdentifier, String domainName) { DhcpPacket pkt = new DhcpOfferPacket( transactionId, broadcast, serverIpAddr, clientIpAddr, mac); pkt.mGateway = gateway; pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; pkt.mServerIdentifier = dhcpServerIdentifier; pkt.mSubnetMask = netMask; pkt.mBroadcastAddress = bcAddr; return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); } /** * Builds a DHCP-ACK packet from the required specified parameters. */ public static ByteBuffer buildAckPacket(int encap, int transactionId, boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr, InetAddress gateway, List dnsServers, InetAddress dhcpServerIdentifier, String domainName) { DhcpPacket pkt = new DhcpAckPacket( transactionId, broadcast, serverIpAddr, clientIpAddr, mac); pkt.mGateway = gateway; pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; pkt.mSubnetMask = netMask; pkt.mServerIdentifier = dhcpServerIdentifier; pkt.mBroadcastAddress = bcAddr; return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); } /** * Builds a DHCP-NAK packet from the required specified parameters. */ public static ByteBuffer buildNakPacket(int encap, int transactionId, InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac) { DhcpPacket pkt = new DhcpNakPacket(transactionId, clientIpAddr, serverIpAddr, serverIpAddr, serverIpAddr, mac); pkt.mMessage = "requested address not available"; pkt.mRequestedIp = clientIpAddr; return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); } /** * Builds a DHCP-REQUEST packet from the required specified parameters. */ public static ByteBuffer buildRequestPacket(int encap, int transactionId, InetAddress clientIp, boolean broadcast, byte[] clientMac, InetAddress requestedIpAddress, InetAddress serverIdentifier, byte[] requestedParams, String hostName) { DhcpPacket pkt = new DhcpRequestPacket(transactionId, clientIp, clientMac, broadcast); pkt.mRequestedIp = requestedIpAddress; pkt.mServerIdentifier = serverIdentifier; pkt.mHostName = hostName; pkt.mRequestedParams = requestedParams; ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); return result; } }