Skip to content

Instantly share code, notes, and snippets.

@salanki
Created July 6, 2011 21:58
Show Gist options
  • Save salanki/1068451 to your computer and use it in GitHub Desktop.
Save salanki/1068451 to your computer and use it in GitHub Desktop.
Mac2IP implementation
package com.proceranetworks.psm.leasepoller
package twpoller
import java.net.InetAddress
import java.nio.ByteBuffer
object ByteBufferImplicits {
implicit def byteBuffer2PimpedByteBuffer(buffer: ByteBuffer): PimpedByteBuffer = new PimpedByteBuffer(buffer)
}
class PimpedByteBuffer(buffer: ByteBuffer) {
def getUnsigned() = buffer.get() & 0xFF
}
class Mac2IpException(msg: String) extends RuntimeException(msg)
class MalformatedPacketException(msg: String) extends Mac2IpException(msg)
/** Response from Mac2Ip server */
object Response {
import ByteBufferImplicits._
def apply(packet: ByteBuffer) = {
try {
packet.position(16) /* Skip first 16 bytes of padding */
packet.get() match { /* Protocol version */
case 0x02 =>
case other => throw new MalformatedPacketException("Protocol version is other than two: " + other)
}
packet.get() match { /* OpType */
case 0x03 =>
case other => throw new MalformatedPacketException("OpType is other than search: " + other)
}
val id = packet.getInt() /* Request id */
packet.get() match { /* Header response type */
case 0x00 =>
case other => throw new MalformatedPacketException("Header response type is different than 0: " + other)
}
val recordCount = packet.getUnsigned()
val records = for (x <- Range(0, recordCount)) yield Record(packet)
new Response(id, records)
} catch {
case _: java.lang.IndexOutOfBoundsException => throw new MalformatedPacketException("Packet too short")
case _: java.nio.BufferUnderflowException => throw new MalformatedPacketException("Packet too short")
}
}
}
/** Response from Mac2Ip server */
class Response(val id: Int, val records: IndexedSeq[Record]) {
override def toString = "Id: " + id + " Records:" + records
}
sealed abstract class RecordType
sealed abstract class CpeRecord extends RecordType
sealed abstract class CmRecord extends RecordType
object GenericCpeRecord extends CpeRecord
object UnknownCmRecord extends CmRecord { override def toString = "Unknown" }
object Docsis10Record extends CmRecord { override def toString = "DOCSIS10" }
object Docsis11Record extends CmRecord { override def toString = "DOCSIS11" }
object Docsis20Record extends CmRecord { override def toString = "DOCSIS20" }
object Docsis30Record extends CmRecord { override def toString = "DOCSIS30" }
/** Record within a response */
object Record {
import ByteBufferImplicits._
def apply(packet: ByteBuffer) = {
try {
val ipBuf = new Array[Byte](4)
val length = packet.getUnsigned()
val recordType = packet.getUnsigned() match {
case 0x01 => UnknownCmRecord
case 0x02 => Docsis10Record
case 0x03 => Docsis11Record
case 0x04 => Docsis20Record
case 0x05 => Docsis30Record
case 0x81 => GenericCpeRecord
case 0x82 => GenericCpeRecord
case 0x83 => GenericCpeRecord
case 0x84 => GenericCpeRecord
case 0x85 => GenericCpeRecord
case other => throw new MalformatedPacketException("Unknown record type: " + other)
}
/* IP Address */
packet.get(ipBuf, 0, 4)
val ip = InetAddress.getByAddress(ipBuf)
val mac = List(packet.get(), packet.get(), packet.get(), packet.get(), packet.get(), packet.get())
val relayAgentMac = List(packet.get(), packet.get(), packet.get(), packet.get(), packet.get(), packet.get())
val leaseTime = packet.getInt()
val leaseGrantTime = packet.getInt()
/* Gateway IP */
packet.get(ipBuf, 0, 4)
val gatewayIp = InetAddress.getByAddress(ipBuf)
/* DHCP Server IP */
packet.get(ipBuf, 0, 4)
val dhcpIp = InetAddress.getByAddress(ipBuf)
val netmask = packet.getUnsigned()
val flags = packet.get()
val lastDiscover = packet.getInt()
val lastRenew = packet.getInt()
val mTime = packet.getInt()
val firstSeen = packet.getInt()
/* Variable length fields */
val bootfile: Option[String] = packet.getUnsigned() match {
case 0 => None
case len =>
packet.getUnsigned() match { /* OpCode */
case 0x43 => { /* bootfile */
packet.getUnsigned() match { /* Content length */
case 0 => None /* No data, weird */
case len => {
val bfBuf = new Array[Byte](len)
packet.get(bfBuf, 0, len)
Some(new String(bfBuf))
}
}
}
case 0x67 => None /* Vendor specific, unimplemented */
case opcode => println("Unimplemented opcode: " + opcode); None
}
}
new Record(recordType, ip, mac, relayAgentMac, leaseTime, leaseGrantTime, dhcpIp, flags, lastDiscover, lastRenew, mTime, firstSeen, bootfile)
} catch {
case e: java.lang.IndexOutOfBoundsException => throw new MalformatedPacketException("Packet too short: " + e.toString)
case e: java.nio.BufferUnderflowException => throw new MalformatedPacketException("Packet too short: " + e.toString)
}
}
}
/** Record within a response */
class Record(val recordType: RecordType, val ip: InetAddress, val mac: List[Byte], val relayAgentMac: List[Byte], val leaseTime: Int, val leaseGrantTime: Int, val dhcpIp: InetAddress, val flags: Byte, val lastDiscover: Int, val lastRenew: Int, val mTime: Int, val firstSeen: Int, val bootfile: Option[String]) {
override def toString = "IP: %s Type: %s MAC: %s bootfile: %s".format(ip.getHostAddress, recordType, mac, bootfile)
}
sealed protected abstract class LookupParameter
case class LookupIp(ip: InetAddress) extends LookupParameter
/**
* Mac2Ip lookup request
* <p>
* Lookups are exhaustive
*/
case class Request(id: Int, lookupTarget: LookupParameter) {
def getBytes = {
var bytes = Vector[Byte]()
for (_ <- Range(0, 16)) { bytes :+= 0.byteValue } /* Pad 16 bytes of zeros */
bytes :+= 0x02.byteValue /* Version 2 */
bytes :+= 0x02.byteValue /* Op type search */
bytes ++= ByteBuffer.allocate(4).putInt(id).array() /* Request id. It is really sad that this is the cleanest way to convert an integer to bytes in Java/Scala */
lookupTarget match { /* Search type */
case LookupIp(ip) => {
bytes :+= 0x81.byteValue /* IP + exhaustive */
bytes ++= ip.getAddress /* Append bytes of IP address */
}
}
bytes
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment