# SPDX-License-Identifier: GPL-2.0-or-later # This file is part of Scapy # See https://scapy.net/ for more information # Copyright (C) 2020 Rahul Jadhav # scapy.contrib.description = Routing Protocol for LLNs (RPL) # scapy.contrib.status = loads """ RPL === RFC 6550 - Routing Protocol for Low-Power and Lossy Networks (RPL) draft-ietf-roll-efficient-npdao-17 - Efficient Route Invalidation +----------------------------------------------------------------------+ | RPL Options : Pad1 PadN TIO RIO PIO Tgt TgtDesc DODAGConfig DAGMC ...| +----------------------------------------------------------------------+ | RPL Msgs : DIS DIO DAO DAOACK DCO DCOACK | +----------------------------------------------------------------------+ | ICMPv6 : type 155 RPL | +----------------------------------------------------------------------+ """ from scapy.packet import Packet, bind_layers from scapy.fields import ByteEnumField, ByteField, IP6Field, ShortField, \ BitField, BitEnumField, FieldLenField, StrLenField, IntField, \ ConditionalField from scapy.layers.inet6 import ICMPv6RPL, icmp6ndraprefs, _IP6PrefixField # https://www.iana.org/assignments/rpl/rpl.xhtml#mop RPLMOP = {0: "No Downward routes", 1: "Non-Storing", 2: "Storing with no multicast support", 3: "Storing with multicast support", 4: "P2P Route Discovery"} # https://www.iana.org/assignments/rpl/rpl.xhtml#control-message-options RPLOPTSSTR = {0: "Pad1", 1: "PadN", 2: "DAG Metric Container", 3: "Routing Information", 4: "DODAG Configuration", 5: "RPL Target", 6: "Transit Information", 7: "Solicited Information", 8: "Prefix Information Option", 9: "Target Descriptor", 10: "P2P Route Discovery"} class _RPLGuessOption(Packet): name = "Dummy RPL Option class" class RPLOptRIO(_RPLGuessOption): """ Control Option: Routing Information Option (RIO) """ name = "Routing Information" fields_desc = [ByteEnumField("otype", 3, RPLOPTSSTR), FieldLenField("len", None, length_of="prefix", fmt="B", adjust=lambda pkt, x: x + 6), ByteField("plen", None), BitField("res1", 0, 3), BitEnumField("prf", 0, 2, icmp6ndraprefs), BitField("res2", 0, 3), IntField("rtlifetime", 0xffffffff), _IP6PrefixField("prefix", None)] class RPLOptDODAGConfig(_RPLGuessOption): """ Control Option: DODAG Configuration """ name = "DODAG Configuration" fields_desc = [ByteEnumField("otype", 4, RPLOPTSSTR), ByteField("len", 14), BitField("flags", 0, 4), BitField("A", 0, 1), BitField("PCS", 0, 3), ByteField("DIOIntDoubl", 20), ByteField("DIOIntMin", 3), ByteField("DIORedun", 10), ShortField("MaxRankIncrease", 0), ShortField("MinRankIncrease", 256), ShortField("OCP", 1), ByteField("reserved", 0), ByteField("DefLifetime", 0xff), ShortField("LifetimeUnit", 0xffff)] class RPLOptTgt(_RPLGuessOption): """ Control Option: RPL Target """ name = "RPL Target" fields_desc = [ByteEnumField("otype", 5, RPLOPTSSTR), FieldLenField("len", None, length_of="prefix", fmt="B", adjust=lambda pkt, x: x + 2), ByteField("flags", 0), ByteField("plen", 0), _IP6PrefixField("prefix", None)] class RPLOptTIO(_RPLGuessOption): """ Control Option: Transit Information Option (TIO) """ name = "Transit Information" fields_desc = [ByteEnumField("otype", 6, RPLOPTSSTR), FieldLenField("len", None, length_of="parentaddr", fmt="B", adjust=lambda pkt, x: x + 4), BitField("E", 0, 1), BitField("flags", 0, 7), ByteField("pathcontrol", 0), ByteField("pathseq", 0), ByteField("pathlifetime", 0xff), _IP6PrefixField("parentaddr", None)] class RPLOptSolInfo(_RPLGuessOption): """ Control Option: Solicited Information """ name = "Solicited Information" fields_desc = [ByteEnumField("otype", 7, RPLOPTSSTR), ByteField("len", 19), ByteField("RPLInstanceID", 0), BitField("V", 0, 1), BitField("I", 0, 1), BitField("D", 0, 1), BitField("flags", 0, 5), IP6Field("dodagid", "::1"), ByteField("ver", 0)] class RPLOptPIO(_RPLGuessOption): """ Control Option: Prefix Information Option (PIO) """ name = "Prefix Information" fields_desc = [ByteEnumField("otype", 8, RPLOPTSSTR), ByteField("len", 30), ByteField("plen", 64), BitField("L", 0, 1), BitField("A", 0, 1), BitField("R", 0, 1), BitField("reserved1", 0, 5), IntField("validlifetime", 0xffffffff), IntField("preflifetime", 0xffffffff), IntField("reserved2", 0), IP6Field("prefix", "::1")] class RPLOptTgtDesc(_RPLGuessOption): """ Control Option: RPL Target Descriptor """ name = "RPL Target Descriptor" fields_desc = [ByteEnumField("otype", 9, RPLOPTSSTR), ByteField("len", 4), IntField("descriptor", 0)] class RPLOptPad1(_RPLGuessOption): """ Control Option: Pad 1 byte """ name = "Pad1" fields_desc = [ByteEnumField("otype", 0x00, RPLOPTSSTR)] class RPLOptPadN(_RPLGuessOption): """ Control Option: Pad N bytes """ name = "PadN" fields_desc = [ByteEnumField("otype", 0x01, RPLOPTSSTR), FieldLenField("optlen", None, length_of="optdata", fmt="B"), StrLenField("optdata", "", length_from=lambda pkt: pkt.optlen)] # https://www.iana.org/assignments/rpl/rpl.xhtml#control-message-options RPLOPTS = {0: RPLOptPad1, 1: RPLOptPadN, # 2: RPLOptDAGMC, defined in rpl_metrics.py 3: RPLOptRIO, 4: RPLOptDODAGConfig, 5: RPLOptTgt, 6: RPLOptTIO, 7: RPLOptSolInfo, 8: RPLOptPIO, 9: RPLOptTgtDesc} # RPL Control Message Handling class _RPLGuessMsgType(Packet): name = "Dummy RPL Message class" def guess_payload_class(self, payload): if isinstance(payload, str): otype = ord(payload[0]) else: otype = payload[0] return RPLOPTS.get(otype) class RPLDIS(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: DODAG Information Solicitation (DIS) """ name = "DODAG Information Solicitation" fields_desc = [ByteField("flags", 0), ByteField("reserved", 0)] class RPLDIO(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: DODAG Information Object (DIO) """ name = "DODAG Information Object" fields_desc = [ByteField("RPLInstanceID", 50), ByteField("ver", 0), ShortField("rank", 1), BitField("G", 1, 1), BitField("unused1", 0, 1), BitEnumField("mop", 1, 3, RPLMOP), BitField("prf", 0, 3), ByteField("dtsn", 240), ByteField("flags", 0), ByteField("reserved", 0), IP6Field("dodagid", "::1")] class RPLDAO(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: Destination Advertisement Object (DAO) """ name = "Destination Advertisement Object" fields_desc = [ByteField("RPLInstanceID", 50), BitField("K", 0, 1), BitField("D", 0, 1), BitField("flags", 0, 6), ByteField("reserved", 0), ByteField("daoseq", 1), ConditionalField(IP6Field("dodagid", None), lambda pkt: pkt.D == 1)] class RPLDAOACK(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: Destination Advertisement Object Acknowledgement (DAOACK) """ name = "Destination Advertisement Object Acknowledgement" fields_desc = [ByteField("RPLInstanceID", 50), BitField("D", 0, 1), BitField("reserved", 0, 7), ByteField("daoseq", 1), ByteField("status", 0), ConditionalField(IP6Field("dodagid", None), lambda pkt: pkt.D == 1)] # https://datatracker.ietf.org/doc/draft-ietf-roll-efficient-npdao/ class RPLDCO(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: Destination Cleanup Object (DCO) """ name = "Destination Cleanup Object" fields_desc = [ByteField("RPLInstanceID", 50), BitField("K", 0, 1), BitField("D", 0, 1), BitField("flags", 0, 6), ByteField("status", 0), ByteField("dcoseq", 1), ConditionalField(IP6Field("dodagid", None), lambda pkt: pkt.D == 1)] # https://datatracker.ietf.org/doc/draft-ietf-roll-efficient-npdao/ class RPLDCOACK(_RPLGuessMsgType, _RPLGuessOption): """ Control Message: Destination Cleanup Object Acknowledgement (DCOACK) """ name = "Destination Cleanup Object Acknowledgement" fields_desc = [ByteField("RPLInstanceID", 50), BitField("D", 0, 1), BitField("flags", 0, 7), ByteField("dcoseq", 1), ByteField("status", 0), ConditionalField(IP6Field("dodagid", None), lambda pkt: pkt.D == 1)] # https://www.iana.org/assignments/rpl/rpl.xhtml#control-codes bind_layers(ICMPv6RPL, RPLDIS, code=0) bind_layers(ICMPv6RPL, RPLDIO, code=1) bind_layers(ICMPv6RPL, RPLDAO, code=2) bind_layers(ICMPv6RPL, RPLDAOACK, code=3) bind_layers(ICMPv6RPL, RPLDCO, code=7) bind_layers(ICMPv6RPL, RPLDCOACK, code=8)