Skip to content

Instantly share code, notes, and snippets.

@JonathonReinhart
Created February 9, 2022 03:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JonathonReinhart/b62b39a64f159c0fa043d6b948bc610c to your computer and use it in GitHub Desktop.
Save JonathonReinhart/b62b39a64f159c0fa043d6b948bc610c to your computer and use it in GitHub Desktop.
RFC3442 Classless Route Option (121) parser
#!/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()
@JonathonReinhart
Copy link
Author

Example:

$ python3 parse.py 00:c0:a8:01:fe:18:c0:a8:65:c0:a8:01:28
0.0.0.0/0 via 192.168.1.254
192.168.101.0/24 via 192.168.1.40

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment