Created
July 6, 2011 21:58
-
-
Save salanki/1068451 to your computer and use it in GitHub Desktop.
Mac2IP implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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