-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from compscidr/jason/ipv4options
Ipv4 options
- Loading branch information
Showing
20 changed files
with
1,586 additions
and
37 deletions.
There are no files selected for viewing
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
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
129 changes: 129 additions & 0 deletions
129
knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4Option.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package com.jasonernst.knet.ip.options | ||
|
||
import com.jasonernst.knet.PacketTooShortException | ||
import org.slf4j.LoggerFactory | ||
import java.nio.ByteBuffer | ||
import java.nio.ByteOrder | ||
|
||
// because kotlin doesn't have a direct conversion function apparently... | ||
// https://stackoverflow.com/questions/46401879/boolean-int-conversion-in-kotlin | ||
fun Boolean.toInt() = if (this) 1 else 0 | ||
|
||
/** | ||
* From RFC791, page 15: | ||
* | ||
* The option field is variable in length. There may be zero or more | ||
* options. There are two cases for the format of an option: | ||
* | ||
* Case 1: A single octet of option-type. | ||
* | ||
* Case 2: An option-type octet, an option-length octet, and the | ||
* actual option-data octets. | ||
* | ||
* The option-length octet counts the option-type octet and the | ||
* option-length octet as well as the option-data octets. | ||
* | ||
* The option-type octet is viewed as having 3 fields: | ||
* | ||
* 1 bit copied flag, | ||
* 2 bits option class, | ||
* 5 bits option number. | ||
* | ||
* The copied flag indicates that this option is copied into all | ||
* fragments on fragmentation. | ||
* | ||
* 0 = not copied | ||
* 1 = copied | ||
*/ | ||
abstract class Ipv4Option( | ||
open val isCopied: Boolean = true, | ||
open val optionClass: Ipv4OptionClassType, | ||
open val type: Ipv4OptionType, | ||
open val size: UByte, | ||
) { | ||
companion object { | ||
private val logger = LoggerFactory.getLogger(Ipv4Option::class.java) | ||
|
||
fun parseOptions( | ||
stream: ByteBuffer, | ||
limit: Int = stream.limit(), | ||
): List<Ipv4Option> { | ||
val options = ArrayList<Ipv4Option>() | ||
while (stream.position() + 1 <= limit) { | ||
val kindOctet = stream.get().toUByte() | ||
// high bit is copied flag | ||
val isCopied = kindOctet.toInt() and 0b10000000 == 0b10000000 | ||
val classByte = (kindOctet.toInt() and 0b01100000) shr 5 | ||
val optionClass = Ipv4OptionClassType.fromKind(classByte.toUByte()) | ||
val kind = (kindOctet.toInt() and 0b00011111).toUByte() | ||
if (kind == Ipv4OptionType.EndOfOptionList.kind) { | ||
options.add(Ipv4OptionEndOfOptionList(isCopied, optionClass)) | ||
break | ||
} else if (kind == Ipv4OptionType.NoOperation.kind) { | ||
options.add(Ipv4OptionNoOperation(isCopied, optionClass)) | ||
} else { | ||
if (stream.remaining() < 1) { | ||
throw PacketTooShortException("Can't determine length of ipv4 option because we have no bytes left") | ||
} | ||
// this length includes the previous two bytes which is why we need adjustment | ||
// we don't apply it directly to length because we want to construct the option | ||
// with the correct length which includes the first two fields | ||
val length = stream.get().toUByte() | ||
if (stream.remaining() < length.toInt() - 2) { | ||
throw PacketTooShortException("Can't parse ipv4 option because we don't have enough bytes left for the data") | ||
} | ||
when (kind) { | ||
Ipv4OptionType.Security.kind -> { | ||
options.add(Ipv4OptionSecurity.fromStream(stream, isCopied, optionClass, length)) | ||
} | ||
Ipv4OptionType.LooseSourceRouting.kind -> { | ||
options.add(Ipv4OptionLooseSourceAndRecordRoute.fromStream(stream, isCopied, optionClass, length)) | ||
} | ||
Ipv4OptionType.StrictSourceRouting.kind -> { | ||
options.add(Ipv4OptionStrictSourceAndRecordRoute.fromStream(stream, isCopied, optionClass, length)) | ||
} | ||
Ipv4OptionType.RecordRoute.kind -> { | ||
options.add(Ipv4OptionRecordRoute.fromStream(stream, isCopied, optionClass, length)) | ||
} | ||
Ipv4OptionType.StreamId.kind -> { | ||
options.add(Ipv4OptionStreamIdentifier.fromStream(stream, isCopied, optionClass, length)) | ||
} | ||
Ipv4OptionType.TimeStamp.kind -> { | ||
options.add(Ipv4OptionInternetTimestamp.fromStream(stream, isCopied, optionClass, length)) | ||
} | ||
else -> { | ||
val data = ByteArray(length.toInt() - 2) | ||
stream.get(data) | ||
|
||
val type = | ||
try { | ||
Ipv4OptionType.fromKind(kind) | ||
} catch (e: NoSuchElementException) { | ||
Ipv4OptionType.Unknown | ||
} | ||
options.add(Ipv4OptionUnknown(isCopied, optionClass, type, length, data)) | ||
} | ||
} | ||
} | ||
} | ||
return options | ||
} | ||
} | ||
|
||
open fun toByteArray(order: ByteOrder = ByteOrder.BIG_ENDIAN): ByteArray { | ||
if (size.toInt() < 1) { | ||
throw IllegalArgumentException("Size must be at least 1") | ||
} | ||
val copiedInt = (isCopied.toInt() shl 7) | ||
val classInt = optionClass.kind.toInt() shl 5 | ||
val typeInt = type.kind.toInt() | ||
val typeByte = (copiedInt + classInt + typeInt).toByte() | ||
if (size.toInt() == 1) { | ||
return byteArrayOf(typeByte) | ||
} | ||
val buffer = ByteBuffer.allocate(2) | ||
buffer.put(typeByte) | ||
buffer.put(size.toByte()) | ||
return buffer.array() | ||
} | ||
} |
26 changes: 26 additions & 0 deletions
26
knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionClassType.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.jasonernst.knet.ip.options | ||
|
||
/** | ||
* The option classes are: | ||
* | ||
* 0 = control | ||
* 1 = reserved for future use | ||
* 2 = debugging and measurement | ||
* 3 = reserved for future use | ||
* | ||
* Since we only have 2 bits, any other value makes no sense and should | ||
* rightfully throw an exception when trying to parse it. | ||
*/ | ||
enum class Ipv4OptionClassType( | ||
val kind: UByte, | ||
) { | ||
Control(0u), | ||
Reserved1(1u), | ||
DebuggingAndMeasurement(2u), | ||
Reserved2(3u), | ||
; | ||
|
||
companion object { | ||
fun fromKind(kind: UByte) = entries.first { it.kind == kind } | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
knet/src/main/kotlin/com/jasonernst/knet/ip/options/Ipv4OptionEndOfOptionList.kt
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.jasonernst.knet.ip.options | ||
|
||
/** | ||
* | ||
* From RFC 791: | ||
* | ||
* This option indicates the end of the option list. This might | ||
* not coincide with the end of the internet header according to | ||
* the internet header length. This is used at the end of all | ||
* options, not the end of each option, and need only be used if | ||
* the end of the options would not otherwise coincide with the end | ||
* of the internet header. | ||
* | ||
* May be copied, introduced, or deleted on fragmentation, or for | ||
* any other reason. | ||
*/ | ||
data class Ipv4OptionEndOfOptionList( | ||
override val isCopied: Boolean = false, | ||
override val optionClass: Ipv4OptionClassType = Ipv4OptionClassType.Control, | ||
override val type: Ipv4OptionType = Ipv4OptionType.EndOfOptionList, | ||
override val size: UByte = 1u, | ||
) : Ipv4Option(isCopied, optionClass, type, size) |
Oops, something went wrong.