From d79c849e964759a09c0c0c69635e42af6a5070f6 Mon Sep 17 00:00:00 2001 From: Jan Sebechlebsky <sebechlebskyjan@gmail.com> Date: Fri, 20 Jan 2017 22:01:17 +0100 Subject: [PATCH] Add support for PPP Link Control Protocol --- scapy/layers/ppp.py | 224 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 220 insertions(+), 4 deletions(-) diff --git a/scapy/layers/ppp.py b/scapy/layers/ppp.py index b2e40ca7..e415eebf 100644 --- a/scapy/layers/ppp.py +++ b/scapy/layers/ppp.py @@ -11,13 +11,13 @@ PPP (Point to Point Protocol) import struct from scapy.packet import Packet, bind_layers -from scapy.layers.l2 import Ether, CookedLinux +from scapy.layers.l2 import Ether, CookedLinux, GRE_PPTP from scapy.layers.inet import IP from scapy.layers.inet6 import IPv6 from scapy.fields import BitField, ByteEnumField, ByteField, \ - ConditionalField, FieldLenField, IPField, PacketListField, \ - ShortEnumField, ShortField, StrFixedLenField, StrLenField, XByteField, \ - XShortField + ConditionalField, FieldLenField, IntField, IPField, LenField, \ + PacketListField, PacketField, ShortEnumField, ShortField, \ + StrFixedLenField, StrLenField, XByteField, XShortField class PPPoE(Packet): @@ -339,6 +339,220 @@ class PPP_ECP(Packet): FieldLenField("len" , None, fmt="H", length_of="options", adjust=lambda p,x:x+4 ), PacketListField("options", [], PPP_ECP_Option, length_from=lambda p:p.len-4,) ] +### Link Control Protocol (RFC 1661) + +_PPP_lcptypes = {1: "Configure-Request", + 2: "Configure-Ack", + 3: "Configure-Nak", + 4: "Configure-Reject", + 5: "Terminate-Request", + 6: "Terminate-Ack", + 7: "Code-Reject", + 8: "Protocol-Reject", + 9: "Echo-Request", + 10: "Echo-Reply", + 11: "Discard-Request"} + + +class PPP_LCP(Packet): + name = "PPP Link Control Protocol" + fields_desc = [ByteEnumField("code", 5, _PPP_lcptypes), + XByteField("id", 0), + FieldLenField("len", None, fmt="H", length_of="data", + adjust=lambda p, x: x + 4), + StrLenField("data", "", + length_from=lambda p:p.len-4)] + + def extract_padding(self, pay): + return "",pay + + @classmethod + def dispatch_hook(cls, _pkt = None, *args, **kargs): + if _pkt: + o = ord(_pkt[0]) + if o in [1, 2, 3, 4]: + return PPP_LCP_Configure + elif o in [5,6]: + return PPP_LCP_Terminate + elif o == 7: + return PPP_LCP_Code_Reject + elif o == 8: + return PPP_LCP_Protocol_Reject + elif o in [9, 10]: + return PPP_LCP_Echo + elif o == 11: + return PPP_LCP_Discard_Request + else: + return cls + return cls + + +_PPP_lcp_optiontypes = {1: "Maximum-Receive-Unit", + 2: "Async-Control-Character-Map", + 3: "Authentication-protocol", + 4: "Quality-protocol", + 5: "Magic-number", + 7: "Protocol-Field-Compression", + 8: "Address-and-Control-Field-Compression", + 13: "Callback"} + + +class PPP_LCP_Option(Packet): + name = "PPP LCP Option" + fields_desc = [ByteEnumField("type", None, _PPP_lcp_optiontypes), + FieldLenField("len", None, fmt="B", length_of="data", + adjust=lambda p,x:x+2), + StrLenField("data", None, length_from=lambda p:p.len-2)] + + def extract_padding(self, pay): + return "", pay + + registered_options = {} + + @classmethod + def register_variant(cls): + cls.registered_options[cls.type.default] = cls + + @classmethod + def dispatch_hook(cls, _pkt=None, *args, **kargs): + if _pkt: + o = ord(_pkt[0]) + return cls.registered_options.get(o, cls) + return cls + + +class PPP_LCP_MRU_Option(PPP_LCP_Option): + fields_desc = [ByteEnumField("type", 1, _PPP_lcp_optiontypes), + FieldLenField("len", 4, fmt="B", adjust=lambda p,x:4), + ShortField("max_recv_unit", 1500)] + +_PPP_LCP_auth_protocols = {0xc023: "Password authentication protocol", + 0xc223: "Challenge-response authentication protocol", + 0xc227: "PPP Extensible authentication protocol"} + +_PPP_LCP_CHAP_algorithms = {5: "MD5", + 6: "SHA1", + 128: "MS-CHAP", + 129: "MS-CHAP-v2"} + + +class PPP_LCP_ACCM_Option(PPP_LCP_Option): + fields_desc = [ByteEnumField("type", 2, _PPP_lcp_optiontypes), + FieldLenField("len", 6, fmt="B"), + BitField("accm", 0x00000000, 32)] + + +def adjust_auth_len(pkt, x): + if pkt.auth_protocol == 0xc223: + return 5 + elif pkt.auth_protocol == 0xc023: + return 4 + else: + return x + 4 + + +class PPP_LCP_Auth_Protocol_Option(PPP_LCP_Option): + fields_desc = [ByteEnumField("type", 3, _PPP_lcp_optiontypes), + FieldLenField("len", None, fmt="B", length_of="data", + adjust=adjust_auth_len), + ShortEnumField("auth_protocol", 0xc023, _PPP_LCP_auth_protocols), + ConditionalField(StrLenField("data", '', length_from=lambda p:p.len-4), + lambda p:p.auth_protocol != 0xc223), + ConditionalField(ByteEnumField("algorithm", 5, _PPP_LCP_CHAP_algorithms), + lambda p:p.auth_protocol == 0xc223)] + + +_PPP_LCP_quality_protocols = {0xc025: "Link Quality Report"} + + +class PPP_LCP_Quality_Protocol_Option(PPP_LCP_Option): + fields_desc = [ByteEnumField("type", 4, _PPP_lcp_optiontypes), + FieldLenField("len", None, fmt="B", length_of="data", + adjust=lambda p,x:x+4), + ShortEnumField("quality_protocol", 0xc025, _PPP_LCP_quality_protocols), + StrLenField("data", "", length_from=lambda p:p.len-4)] + + +class PPP_LCP_Magic_Number_Option(PPP_LCP_Option): + fields_desc = [ByteEnumField("type", 5, _PPP_lcp_optiontypes), + FieldLenField("len", 6, fmt="B", adjust = lambda p,x:6), + IntField("magic_number", None)] + + +_PPP_lcp_callback_operations = {0: "Location determined by user authentication", + 1: "Dialing string", + 2: "Location identifier", + 3: "E.164 number", + 4: "Distinguished name"} + + +class PPP_LCP_Callback_Option(PPP_LCP_Option): + fields_desc = [ByteEnumField("type", 13, _PPP_lcp_optiontypes), + FieldLenField("len", None, fmt="B", length_of="message", + adjust=lambda p,x:x+3), + ByteEnumField("operation", 0, _PPP_lcp_callback_operations), + StrLenField("message", "", length_from=lambda p:p.len-3)] + + +class PPP_LCP_Configure(PPP_LCP): + fields_desc = [ByteEnumField("code", 1, _PPP_lcptypes), + XByteField("id", 0), + FieldLenField("len", None, fmt="H", length_of="options", + adjust=lambda p,x:x+4), + PacketListField("options", [], PPP_LCP_Option, + length_from=lambda p:p.len-4)] + + def answers(self, other): + return isinstance(other, PPP_LCP_Configure) and self.code in [2, 3, 4]\ + and other.code == 1 and other.id == self.id + + +class PPP_LCP_Terminate(PPP_LCP): + + def answers(self, other): + return isinstance(other, PPP_LCP_Terminate) and self.code == 6\ + and other.code == 5 and other.id == self.id + + +class PPP_LCP_Code_Reject(PPP_LCP): + fields_desc = [ByteEnumField("code", 7, _PPP_lcptypes), + XByteField("id", 0), + FieldLenField("len", None, fmt="H", length_of="rejected_packet", + adjust=lambda p,x:x+4), + PacketField("rejected_packet", None, PPP_LCP)] + + +class PPP_LCP_Protocol_Reject(PPP_LCP): + fields_desc = [ByteEnumField("code", 8, _PPP_lcptypes), + XByteField("id", 0), + FieldLenField("len", None, fmt="H", length_of="rejected_information", + adjust=lambda p,x:x+6), + ShortEnumField("rejected_protocol", None, _PPP_proto), + PacketField("rejected_information", None, Packet)] + + +class PPP_LCP_Echo(PPP_LCP): + fields_desc = [ByteEnumField("code", 9, _PPP_lcptypes), + XByteField("id", 0), + FieldLenField("len", None, fmt="H", length_of="data", + adjust=lambda p,x:x+8), + IntField("magic_number", None), + StrLenField("data", "", length_from=lambda p:p.len-8)] + + def answers(self, other): + return isinstance(other, PPP_LCP_Echo) and self.code == 10\ + and other.code == 9 and self.id == other.id + + +class PPP_LCP_Discard_Request(PPP_LCP): + fields_desc = [ByteEnumField("code", 11, _PPP_lcptypes), + XByteField("id", 0), + FieldLenField("len", None, fmt="H", length_of="data", + adjust=lambda p,x:x+8), + IntField("magic_number", None), + StrLenField("data", "", length_from=lambda p:p.len-8)] + + bind_layers( Ether, PPPoED, type=0x8863) bind_layers( Ether, PPPoE, type=0x8864) bind_layers( CookedLinux, PPPoED, proto=0x8863) @@ -349,5 +563,7 @@ bind_layers( PPP, IP, proto=0x0021) bind_layers( PPP, IPv6, proto=0x0057) bind_layers( PPP, PPP_IPCP, proto=0x8021) bind_layers( PPP, PPP_ECP, proto=0x8053) +bind_layers( PPP, PPP_LCP, proto=0xc021) bind_layers( Ether, PPP_IPCP, type=0x8021) bind_layers( Ether, PPP_ECP, type=0x8053) +bind_layers( GRE_PPTP, PPP, proto=0x880b) -- GitLab