diff --git a/scapy/contrib/rpl_metrics.py b/scapy/contrib/rpl_metrics.py index 4f8961f6041..d6ed3f02607 100644 --- a/scapy/contrib/rpl_metrics.py +++ b/scapy/contrib/rpl_metrics.py @@ -21,7 +21,7 @@ import struct from scapy.packet import Packet from scapy.fields import ByteEnumField, ByteField, ShortField, BitField, \ - BitEnumField, FieldLenField, StrLenField, IntField, PacketListField + BitEnumField, StrLenField, IntField, PacketListField from scapy.layers.inet6 import _PhantomAutoPadField from scapy.contrib.rpl import RPLOPTSSTR, RPLOPTS, _RPLGuessOption @@ -48,28 +48,6 @@ def post_build(self, pkt, pay): 8: "Link Color"} -class DAGMCObjUnknown(Packet): - """ - Dummy unknown metric/constraint - """ - name = 'Unknown DAGMC Object Option' - fields_desc = [ByteEnumField("otype", 3, DAGMC_OBJTYPE), - FieldLenField("olen", None, length_of="odata", fmt="B"), - StrLenField("odata", "", - length_from=lambda pkt: pkt.olen)] - - @classmethod - def dispatch_hook(cls, _pkt=None, *_, **kargs): - """ - Dispatch hook for DAGMC sub-fields - """ - if _pkt: - opt_type = _pkt[0] # Option type - if opt_type in DAGMC_CLS: - return DAGMC_CLS[opt_type] - return cls - - AGG_RTMETRIC = {0: "additive", 1: "maximum", 2: "minimum", @@ -107,6 +85,31 @@ def extract_padding(self, s): return s[:tmp_len], s[tmp_len:] +class DAGMCObjUnknown(DAGMCObj): + """ + Metric/constraint object of a type without its own class: the + common header of RFC 6551 - 2.1, then Length octets of body + """ + name = 'Unknown DAGMC Object Option' + deprecated_fields = { + "olen": ("len", "2.8.1"), + } + fields_desc = DAGMCObj.fields_desc + [ + StrLenField("odata", "", length_from=lambda pkt: pkt.len) + ] + + @classmethod + def dispatch_hook(cls, _pkt=None, *_, **kargs): + """ + Dispatch hook for DAGMC sub-fields + """ + if _pkt: + opt_type = _pkt[0] # Option type + if opt_type in DAGMC_CLS: + return DAGMC_CLS[opt_type] + return cls + + class RPLDAGMCNSA(DAGMCObj): """ DAG Metric: Node State and Attributes diff --git a/test/contrib/rpl.uts b/test/contrib/rpl.uts index f4190981ab2..87bc28d4a91 100644 --- a/test/contrib/rpl.uts +++ b/test/contrib/rpl.uts @@ -228,3 +228,29 @@ assert raw(p.options[0].payload) == bytes.fromhex("0241") assert p.options[1].HopCount == 4 p.clear_cache() assert raw(p) == raw_mc + += RPL DAG Metric Container with an object of an unassigned type +# An unknown object has the same 4-octet header as the others (RFC 6551, 2.1) + +# Type 9 with a 2-octet body, then Hop Count +raw_mc = bytes.fromhex("020c09000002abcd" + "030000020003") +p = RPLOptDAGMC(raw_mc) +assert [type(o) for o in p.options] == [DAGMCObjUnknown, RPLDAGMCHopCount] +assert p.options[0].otype == 9 +assert p.options[0].len == 2 +assert p.options[0].odata == b"\xab\xcd" +assert p.options[0].olen == 2 # deprecated name of len +assert p.options[1].HopCount == 3 +p.clear_cache() +assert raw(p) == raw_mc + +# Type 9 with an empty body between Hop Count and Link ETX +raw_mc = bytes.fromhex("0210030000020003" + "09000000" + "070000020100") +p = RPLOptDAGMC(raw_mc) +assert [type(o) for o in p.options] == [RPLDAGMCHopCount, DAGMCObjUnknown, RPLDAGMCLinkETX] +assert p.options[2].ETX == 256 + +# Building fills in the Length of the body +assert raw(DAGMCObjUnknown(otype=9, P=1, odata=b"\xab\xcd")) == bytes.fromhex("09040002abcd") +mc = RPLOptDAGMC(options=[DAGMCObjUnknown(otype=9, odata=b"\xab\xcd"), RPLDAGMCHopCount(HopCount=3)]) +assert raw(mc) == bytes.fromhex("020c09000002abcd030000020003")