autotest: dhcp: Add test for classless static routes
Add parsing and serialization of the DHCP classless static route
option. Add a unit test for the above. Add verification that
classless static route options are converted into a default route.
Add a test that uses the classless static route option.
BUG=chromium-os:25908
TEST=Unit test + run auto test
CQ-DEPEND=I6acecb09258229d84d4aa43372a1dc13fbac1df5
Change-Id: Id371841657c45bc3033fb5cc9944d8af5b032402
Reviewed-on: https://gerrit.chromium.org/gerrit/38204
Commit-Ready: Paul Stewart <[email protected]>
Reviewed-by: Paul Stewart <[email protected]>
Tested-by: Paul Stewart <[email protected]>
diff --git a/client/cros/dhcp_packet.py b/client/cros/dhcp_packet.py
index e5496d7..e218cf9 100644
--- a/client/cros/dhcp_packet.py
+++ b/client/cros/dhcp_packet.py
@@ -110,6 +110,53 @@
return [ord(c) for c in byte_string]
+class ClasslessStaticRoutesOption(Option):
+ """
+ This is a RFC 3442 compliant classless static route option parser and
+ serializer. The symbolic "value" packed and unpacked from this class
+ is a list (prefix_size, destination, router) tuples.
+ """
+
+ @staticmethod
+ def pack(value):
+ route_list = value
+ byte_string = ""
+ for prefix_size, destination, router in route_list:
+ byte_string += chr(prefix_size)
+ # Encode only the significant octets of the destination
+ # that fall within the prefix.
+ destination_address_count = (prefix_size + 7) / 8
+ destination_address = socket.inet_aton(destination)
+ byte_string += destination_address[:destination_address_count]
+ byte_string += socket.inet_aton(router)
+
+ return byte_string
+
+ @staticmethod
+ def unpack(byte_string):
+ route_list = []
+ offset = 0
+ while offset < len(byte_string):
+ prefix_size = ord(byte_string[offset])
+ destination_address_count = (prefix_size + 7) / 8
+ entry_end = offset + 1 + destination_address_count + 4
+ if entry_end > len(byte_string):
+ raise Exception("Classless domain list is corrupted.")
+ offset += 1
+ destination_address_end = offset + destination_address_count
+ destination_address = byte_string[offset:destination_address_end]
+ # Pad the destination address bytes with zero byte octets to
+ # fill out an IPv4 address.
+ destination_address += '\x00' * (4 - destination_address_count)
+ router_address = byte_string[destination_address_end:entry_end]
+ route_list.append((prefix_size,
+ socket.inet_ntoa(destination_address),
+ socket.inet_ntoa(router_address)))
+ offset = entry_end
+
+ return route_list
+
+
class DomainListOption(Option):
"""
This is a RFC 1035 compliant domain list option parser and serializer.
@@ -296,6 +343,8 @@
OPTION_TFTP_SERVER_NAME = RawOption("tftp_server_name", 66)
OPTION_BOOTFILE_NAME = RawOption("bootfile_name", 67)
OPTION_DNS_DOMAIN_SEARCH_LIST = DomainListOption("domain_search_list", 119)
+OPTION_CLASSLESS_STATIC_ROUTES = ClasslessStaticRoutesOption(
+ "classless_static_routes", 121)
# Unlike every other option, which are tuples like:
# <number, length in bytes, data>, the pad and end options are just
@@ -410,6 +459,7 @@
OPTION_TFTP_SERVER_NAME,
OPTION_BOOTFILE_NAME,
OPTION_DNS_DOMAIN_SEARCH_LIST,
+ OPTION_CLASSLESS_STATIC_ROUTES,
]
def get_dhcp_option_by_number(number):