Created
February 9, 2022 03:09
-
-
Save JonathonReinhart/b62b39a64f159c0fa043d6b948bc610c to your computer and use it in GitHub Desktop.
RFC3442 Classless Route Option (121) parser
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
#!/usr/bin/env python3 | |
# https://www.rfc-editor.org/rfc/rfc3442.html | |
from binascii import unhexlify | |
from socket import inet_ntop, AF_INET | |
from io import BytesIO | |
import math | |
""" | |
Classless Route Option Format | |
The code for this option is 121, and its minimum length is 5 bytes. | |
This option can contain one or more static routes, each of which | |
consists of a destination descriptor and the IP address of the router | |
that should be used to reach that destination. | |
Code Len Destination 1 Router 1 | |
+-----+---+----+-----+----+----+----+----+----+ | |
| 121 | n | d1 | ... | dN | r1 | r2 | r3 | r4 | | |
+-----+---+----+-----+----+----+----+----+----+ | |
Destination 2 Router 2 | |
+----+-----+----+----+----+----+----+ | |
| d1 | ... | dN | r1 | r2 | r3 | r4 | | |
+----+-----+----+----+----+----+----+ | |
In the above example, two static routes are specified. | |
Destination descriptors describe the IP subnet number and subnet mask | |
of a particular destination using a compact encoding. This encoding | |
consists of one octet describing the width of the subnet mask, | |
followed by all the significant octets of the subnet number. | |
The width of the subnet mask describes the number of one bits in the | |
mask, so for example a subnet with a subnet number of 10.0.127.0 and | |
a netmask of 255.255.255.0 would have a subnet mask width of 24. | |
The significant portion of the subnet number is simply all of the | |
octets of the subnet number where the corresponding octet in the | |
subnet mask is non-zero. The number of significant octets is the | |
width of the subnet mask divided by eight, rounding up, as shown in | |
the following table: | |
Width of subnet mask Number of significant octets | |
0 0 | |
1- 8 1 | |
9-16 2 | |
17-24 3 | |
25-32 4 | |
The following table contains some examples of how various subnet | |
number/mask combinations can be encoded: | |
Subnet number Subnet mask Destination descriptor | |
0 0 0 | |
10.0.0.0 255.0.0.0 8.10 | |
10.0.0.0 255.255.255.0 24.10.0.0 | |
10.17.0.0 255.255.0.0 16.10.17 | |
10.27.129.0 255.255.255.0 24.10.27.129 | |
10.229.0.128 255.255.255.128 25.10.229.0.128 | |
10.198.122.47 255.255.255.255 32.10.198.122.47 | |
""" | |
class BinaryReader: | |
def __init__(self, f, default_byteorder=None): | |
if not isinstance(f.read(0), bytes): | |
raise ValueError("File not open in binary mode") | |
self.f = f | |
self.default_byteorder = default_byteorder | |
@classmethod | |
def from_bytes(cls, data, **kw): | |
return cls(BytesIO(data), **kw) | |
def read(self, size=-1): | |
return self.f.read(size) | |
def readn(self, size): | |
"""Read exactly size bytes""" | |
result = b"" | |
remain = size | |
while remain: | |
b = self.read(remain) | |
if not b: | |
raise EOFError() | |
result += b | |
assert len(b) <= remain | |
remain -= len(b) | |
assert len(result) == size | |
return result | |
def read_int(self, size, byteorder=None, signed=False): | |
byteorder = byteorder or self.default_byteorder | |
if not byteorder: | |
raise ValueError("byteorder not provided and default_byteorder not set") | |
return int.from_bytes(self.readn(size), byteorder, signed=signed) | |
def read_u8(self, byteorder=None): | |
return self.read_int(1, byteorder) | |
def parse_args(): | |
import argparse | |
ap = argparse.ArgumentParser() | |
ap.add_argument('hexstr') | |
return ap.parse_args() | |
def main(): | |
args = parse_args() | |
data = unhexlify(args.hexstr.replace(':', '')) | |
br = BinaryReader.from_bytes(data, default_byteorder='big') | |
while True: | |
try: | |
width = br.read_u8() | |
except EOFError: | |
break | |
nsigoct = math.ceil(width / 8) | |
dest = inet_ntop(AF_INET, br.readn(nsigoct).ljust(4, b'\x00')) | |
rtr = inet_ntop(AF_INET, br.readn(4)) | |
print(f"{dest}/{width} via {rtr}") | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example: