1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
5
6"""
7PPP (Point to Point Protocol)
8
9[RFC 1661]
10"""
11
12import struct
13from scapy.config import conf
14from scapy.data import DLT_PPP, DLT_PPP_SERIAL, DLT_PPP_ETHER, \
15 DLT_PPP_WITH_DIR
16from scapy.compat import orb
17from scapy.packet import Packet, bind_layers
18from scapy.layers.eap import EAP
19from scapy.layers.l2 import Ether, CookedLinux, GRE_PPTP
20from scapy.layers.inet import IP
21from scapy.layers.inet6 import IPv6
22from scapy.fields import (
23 BitField,
24 ByteEnumField,
25 ByteField,
26 ConditionalField,
27 EnumField,
28 FieldLenField,
29 IPField,
30 IntField,
31 OUIField,
32 PacketField,
33 PacketListField,
34 ShortEnumField,
35 ShortField,
36 StrLenField,
37 XByteField,
38 XShortField,
39 XStrLenField,
40)
41
42
43class PPPoE(Packet):
44 name = "PPP over Ethernet"
45 fields_desc = [BitField("version", 1, 4),
46 BitField("type", 1, 4),
47 ByteEnumField("code", 0, {0: "Session"}),
48 XShortField("sessionid", 0x0),
49 ShortField("len", None)]
50
51 def post_build(self, p, pay):
52 p += pay
53 if self.len is None:
54 tmp_len = len(p) - 6
55 p = p[:4] + struct.pack("!H", tmp_len) + p[6:]
56 return p
57
58
59# PPPoE Active Discovery Code fields (RFC2516, RFC5578)
60class PPPoED(PPPoE):
61 name = "PPP over Ethernet Discovery"
62
63 code_list = {0x00: "PPP Session Stage",
64 0x09: "PPPoE Active Discovery Initiation (PADI)",
65 0x07: "PPPoE Active Discovery Offer (PADO)",
66 0x0a: "PPPoE Active Discovery Session-Grant (PADG)",
67 0x0b: "PPPoE Active Discovery Session-Credit Response (PADC)",
68 0x0c: "PPPoE Active Discovery Quality (PADQ)",
69 0x19: "PPPoE Active Discovery Request (PADR)",
70 0x65: "PPPoE Active Discovery Session-confirmation (PADS)",
71 0xa7: "PPPoE Active Discovery Terminate (PADT)"}
72
73 fields_desc = [BitField("version", 1, 4),
74 BitField("type", 1, 4),
75 ByteEnumField("code", 0x09, code_list),
76 XShortField("sessionid", 0x0),
77 ShortField("len", None)]
78
79 def extract_padding(self, s):
80 return s[:self.len], s[self.len:]
81
82 def mysummary(self):
83 return self.sprintf("%code%")
84
85
86# PPPoE Tag types (RFC2516, RFC4638, RFC5578)
87class PPPoETag(Packet):
88 name = "PPPoE Tag"
89
90 tag_list = {0x0000: 'End-Of-List',
91 0x0101: 'Service-Name',
92 0x0102: 'AC-Name',
93 0x0103: 'Host-Uniq',
94 0x0104: 'AC-Cookie',
95 0x0105: 'Vendor-Specific',
96 0x0106: 'Credits',
97 0x0107: 'Metrics',
98 0x0108: 'Sequence Number',
99 0x0109: 'Credit Scale Factor',
100 0x0110: 'Relay-Session-Id',
101 0x0120: 'PPP-Max-Payload',
102 0x0201: 'Service-Name-Error',
103 0x0202: 'AC-System-Error',
104 0x0203: 'Generic-Error'}
105
106 fields_desc = [
107 ShortEnumField('tag_type', None, tag_list),
108 FieldLenField('tag_len', None, length_of='tag_value', fmt='H'),
109 StrLenField('tag_value', '', length_from=lambda pkt:pkt.tag_len)
110 ]
111
112 def extract_padding(self, s):
113 return '', s
114
115
116class PPPoED_Tags(Packet):
117 name = "PPPoE Tag List"
118 fields_desc = [PacketListField('tag_list', None, PPPoETag)]
119
120 def mysummary(self):
121 return "PPPoE Tags" + ", ".join(
122 x.sprintf("%tag_type%") for x in self.tag_list
123 ), [PPPoED]
124
125
126_PPP_PROTOCOLS = {
127 0x0001: "Padding Protocol",
128 0x0003: "ROHC small-CID [RFC3095]",
129 0x0005: "ROHC large-CID [RFC3095]",
130 0x0021: "Internet Protocol version 4",
131 0x0023: "OSI Network Layer",
132 0x0025: "Xerox NS IDP",
133 0x0027: "DECnet Phase IV",
134 0x0029: "Appletalk",
135 0x002b: "Novell IPX",
136 0x002d: "Van Jacobson Compressed TCP/IP",
137 0x002f: "Van Jacobson Uncompressed TCP/IP",
138 0x0031: "Bridging PDU",
139 0x0033: "Stream Protocol (ST-II)",
140 0x0035: "Banyan Vines",
141 0x0037: "reserved (until 1993) [Typo in RFC1172]",
142 0x0039: "AppleTalk EDDP",
143 0x003b: "AppleTalk SmartBuffered",
144 0x003d: "Multi-Link [RFC1717]",
145 0x003f: "NETBIOS Framing",
146 0x0041: "Cisco Systems",
147 0x0043: "Ascom Timeplex",
148 0x0045: "Fujitsu Link Backup and Load Balancing (LBLB)",
149 0x0047: "DCA Remote Lan",
150 0x0049: "Serial Data Transport Protocol (PPP-SDTP)",
151 0x004b: "SNA over 802.2",
152 0x004d: "SNA",
153 0x004f: "IPv6 Header Compression",
154 0x0051: "KNX Bridging Data [ianp]",
155 0x0053: "Encryption [Meyer]",
156 0x0055: "Individual Link Encryption [Meyer]",
157 0x0057: "Internet Protocol version 6 [Hinden]",
158 0x0059: "PPP Muxing [RFC3153]",
159 0x005b: "Vendor-Specific Network Protocol (VSNP) [RFC3772]",
160 0x0061: "RTP IPHC Full Header [RFC3544]",
161 0x0063: "RTP IPHC Compressed TCP [RFC3544]",
162 0x0065: "RTP IPHC Compressed Non TCP [RFC3544]",
163 0x0067: "RTP IPHC Compressed UDP 8 [RFC3544]",
164 0x0069: "RTP IPHC Compressed RTP 8 [RFC3544]",
165 0x006f: "Stampede Bridging",
166 0x0071: "Reserved [Fox]",
167 0x0073: "MP+ Protocol [Smith]",
168 0x007d: "reserved (Control Escape) [RFC1661]",
169 0x007f: "reserved (compression inefficient [RFC1662]",
170 0x0081: "Reserved Until 20-Oct-2000 [IANA]",
171 0x0083: "Reserved Until 20-Oct-2000 [IANA]",
172 0x00c1: "NTCITS IPI [Ungar]",
173 0x00cf: "reserved (PPP NLID)",
174 0x00fb: "single link compression in multilink [RFC1962]",
175 0x00fd: "compressed datagram [RFC1962]",
176 0x00ff: "reserved (compression inefficient)",
177 0x0201: "802.1d Hello Packets",
178 0x0203: "IBM Source Routing BPDU",
179 0x0205: "DEC LANBridge100 Spanning Tree",
180 0x0207: "Cisco Discovery Protocol [Sastry]",
181 0x0209: "Netcs Twin Routing [Korfmacher]",
182 0x020b: "STP - Scheduled Transfer Protocol [Segal]",
183 0x020d: "EDP - Extreme Discovery Protocol [Grosser]",
184 0x0211: "Optical Supervisory Channel Protocol (OSCP)[Prasad]",
185 0x0213: "Optical Supervisory Channel Protocol (OSCP)[Prasad]",
186 0x0231: "Luxcom",
187 0x0233: "Sigma Network Systems",
188 0x0235: "Apple Client Server Protocol [Ridenour]",
189 0x0281: "MPLS Unicast [RFC3032] ",
190 0x0283: "MPLS Multicast [RFC3032]",
191 0x0285: "IEEE p1284.4 standard - data packets [Batchelder]",
192 0x0287: "ETSI TETRA Network Protocol Type 1 [Nieminen]",
193 0x0289: "Multichannel Flow Treatment Protocol [McCann]",
194 0x2063: "RTP IPHC Compressed TCP No Delta [RFC3544]",
195 0x2065: "RTP IPHC Context State [RFC3544]",
196 0x2067: "RTP IPHC Compressed UDP 16 [RFC3544]",
197 0x2069: "RTP IPHC Compressed RTP 16 [RFC3544]",
198 0x4001: "Cray Communications Control Protocol [Stage]",
199 0x4003: "CDPD Mobile Network Registration Protocol [Quick]",
200 0x4005: "Expand accelerator protocol [Rachmani]",
201 0x4007: "ODSICP NCP [Arvind]",
202 0x4009: "DOCSIS DLL [Gaedtke]",
203 0x400B: "Cetacean Network Detection Protocol [Siller]",
204 0x4021: "Stacker LZS [Simpson]",
205 0x4023: "RefTek Protocol [Banfill]",
206 0x4025: "Fibre Channel [Rajagopal]",
207 0x4027: "EMIT Protocols [Eastham]",
208 0x405b: "Vendor-Specific Protocol (VSP) [RFC3772]",
209 0x8021: "Internet Protocol Control Protocol",
210 0x8023: "OSI Network Layer Control Protocol",
211 0x8025: "Xerox NS IDP Control Protocol",
212 0x8027: "DECnet Phase IV Control Protocol",
213 0x8029: "Appletalk Control Protocol",
214 0x802b: "Novell IPX Control Protocol",
215 0x802d: "reserved",
216 0x802f: "reserved",
217 0x8031: "Bridging NCP",
218 0x8033: "Stream Protocol Control Protocol",
219 0x8035: "Banyan Vines Control Protocol",
220 0x8037: "reserved (until 1993)",
221 0x8039: "reserved",
222 0x803b: "reserved",
223 0x803d: "Multi-Link Control Protocol",
224 0x803f: "NETBIOS Framing Control Protocol",
225 0x8041: "Cisco Systems Control Protocol",
226 0x8043: "Ascom Timeplex",
227 0x8045: "Fujitsu LBLB Control Protocol",
228 0x8047: "DCA Remote Lan Network Control Protocol (RLNCP)",
229 0x8049: "Serial Data Control Protocol (PPP-SDCP)",
230 0x804b: "SNA over 802.2 Control Protocol",
231 0x804d: "SNA Control Protocol",
232 0x804f: "IP6 Header Compression Control Protocol",
233 0x8051: "KNX Bridging Control Protocol [ianp]",
234 0x8053: "Encryption Control Protocol [Meyer]",
235 0x8055: "Individual Link Encryption Control Protocol [Meyer]",
236 0x8057: "IPv6 Control Protovol [Hinden]",
237 0x8059: "PPP Muxing Control Protocol [RFC3153]",
238 0x805b: "Vendor-Specific Network Control Protocol (VSNCP) [RFC3772]",
239 0x806f: "Stampede Bridging Control Protocol",
240 0x8073: "MP+ Control Protocol [Smith]",
241 0x8071: "Reserved [Fox]",
242 0x807d: "Not Used - reserved [RFC1661]",
243 0x8081: "Reserved Until 20-Oct-2000 [IANA]",
244 0x8083: "Reserved Until 20-Oct-2000 [IANA]",
245 0x80c1: "NTCITS IPI Control Protocol [Ungar]",
246 0x80cf: "Not Used - reserved [RFC1661]",
247 0x80fb: "single link compression in multilink control [RFC1962]",
248 0x80fd: "Compression Control Protocol [RFC1962]",
249 0x80ff: "Not Used - reserved [RFC1661]",
250 0x8207: "Cisco Discovery Protocol Control [Sastry]",
251 0x8209: "Netcs Twin Routing [Korfmacher]",
252 0x820b: "STP - Control Protocol [Segal]",
253 0x820d: "EDPCP - Extreme Discovery Protocol Ctrl Prtcl [Grosser]",
254 0x8235: "Apple Client Server Protocol Control [Ridenour]",
255 0x8281: "MPLSCP [RFC3032]",
256 0x8285: "IEEE p1284.4 standard - Protocol Control [Batchelder]",
257 0x8287: "ETSI TETRA TNP1 Control Protocol [Nieminen]",
258 0x8289: "Multichannel Flow Treatment Protocol [McCann]",
259 0xc021: "Link Control Protocol",
260 0xc023: "Password Authentication Protocol",
261 0xc025: "Link Quality Report",
262 0xc027: "Shiva Password Authentication Protocol",
263 0xc029: "CallBack Control Protocol (CBCP)",
264 0xc02b: "BACP Bandwidth Allocation Control Protocol [RFC2125]",
265 0xc02d: "BAP [RFC2125]",
266 0xc05b: "Vendor-Specific Authentication Protocol (VSAP) [RFC3772]",
267 0xc081: "Container Control Protocol [KEN]",
268 0xc223: "Challenge Handshake Authentication Protocol",
269 0xc225: "RSA Authentication Protocol [Narayana]",
270 0xc227: "Extensible Authentication Protocol [RFC2284]",
271 0xc229: "Mitsubishi Security Info Exch Ptcl (SIEP) [Seno]",
272 0xc26f: "Stampede Bridging Authorization Protocol",
273 0xc281: "Proprietary Authentication Protocol [KEN]",
274 0xc283: "Proprietary Authentication Protocol [Tackabury]",
275 0xc481: "Proprietary Node ID Authentication Protocol [KEN]",
276}
277
278
279class HDLC(Packet):
280 fields_desc = [XByteField("address", 0xff),
281 XByteField("control", 0x03)]
282
283
284# LINKTYPE_PPP_WITH_DIR
285class DIR_PPP(Packet):
286 fields_desc = [ByteEnumField("direction", 0, ["received", "sent"])]
287
288
289class _PPPProtoField(EnumField):
290 """
291 A field that can be either Byte or Short, depending on the PPP RFC.
292
293 See RFC 1661 section 2
294 <https://tools.ietf.org/html/rfc1661#section-2>
295
296 The generated proto field is two bytes when not specified, or when specified
297 as an integer or a string:
298 PPP()
299 PPP(proto=0x21)
300 PPP(proto="Internet Protocol version 4")
301 To explicitly forge a one byte proto field, use the bytes representation:
302 PPP(proto=b'\x21')
303 """
304 def getfield(self, pkt, s):
305 if ord(s[:1]) & 0x01:
306 self.fmt = "!B"
307 self.sz = 1
308 else:
309 self.fmt = "!H"
310 self.sz = 2
311 self.struct = struct.Struct(self.fmt)
312 return super(_PPPProtoField, self).getfield(pkt, s)
313
314 def addfield(self, pkt, s, val):
315 if isinstance(val, bytes):
316 if len(val) == 1:
317 fmt, sz = "!B", 1
318 elif len(val) == 2:
319 fmt, sz = "!H", 2
320 else:
321 raise TypeError('Invalid length for PPP proto')
322 val = struct.Struct(fmt).unpack(val)[0]
323 else:
324 fmt, sz = "!H", 2
325 self.fmt = fmt
326 self.sz = sz
327 self.struct = struct.Struct(self.fmt)
328 return super(_PPPProtoField, self).addfield(pkt, s, val)
329
330
331class PPP(Packet):
332 name = "PPP Link Layer"
333 fields_desc = [_PPPProtoField("proto", 0x0021, _PPP_PROTOCOLS)]
334
335 @classmethod
336 def dispatch_hook(cls, _pkt=None, *args, **kargs):
337 if _pkt and _pkt[:1] == b'\xff':
338 return HDLC
339 return cls
340
341
342_PPP_conftypes = {1: "Configure-Request",
343 2: "Configure-Ack",
344 3: "Configure-Nak",
345 4: "Configure-Reject",
346 5: "Terminate-Request",
347 6: "Terminate-Ack",
348 7: "Code-Reject",
349 8: "Protocol-Reject",
350 9: "Echo-Request",
351 10: "Echo-Reply",
352 11: "Discard-Request",
353 14: "Reset-Request",
354 15: "Reset-Ack",
355 }
356
357
358# PPP IPCP stuff (RFC 1332)
359
360# All IPCP options are defined below (names and associated classes)
361_PPP_ipcpopttypes = {1: "IP-Addresses (Deprecated)",
362 2: "IP-Compression-Protocol",
363 3: "IP-Address",
364 # not implemented, present for completeness
365 4: "Mobile-IPv4",
366 129: "Primary-DNS-Address",
367 130: "Primary-NBNS-Address",
368 131: "Secondary-DNS-Address",
369 132: "Secondary-NBNS-Address"}
370
371
372class PPP_IPCP_Option(Packet):
373 name = "PPP IPCP Option"
374 fields_desc = [
375 ByteEnumField("type", None, _PPP_ipcpopttypes),
376 FieldLenField("len", None, length_of="data", fmt="B",
377 adjust=lambda _, val: val + 2),
378 StrLenField("data", "", length_from=lambda pkt: max(0, pkt.len - 2)),
379 ]
380
381 def extract_padding(self, pay):
382 return b"", pay
383
384 registered_options = {}
385
386 @classmethod
387 def register_variant(cls):
388 cls.registered_options[cls.type.default] = cls
389
390 @classmethod
391 def dispatch_hook(cls, _pkt=None, *args, **kargs):
392 if _pkt:
393 o = orb(_pkt[0])
394 return cls.registered_options.get(o, cls)
395 return cls
396
397
398class PPP_IPCP_Option_IPAddress(PPP_IPCP_Option):
399 name = "PPP IPCP Option: IP Address"
400 fields_desc = [
401 ByteEnumField("type", 3, _PPP_ipcpopttypes),
402 FieldLenField("len", None, length_of="data", fmt="B",
403 adjust=lambda _, val: val + 2),
404 IPField("data", "0.0.0.0"),
405 StrLenField("garbage", "", length_from=lambda pkt: pkt.len - 6),
406 ]
407
408
409class PPP_IPCP_Option_DNS1(PPP_IPCP_Option_IPAddress):
410 name = "PPP IPCP Option: DNS1 Address"
411 type = 129
412
413
414class PPP_IPCP_Option_DNS2(PPP_IPCP_Option_IPAddress):
415 name = "PPP IPCP Option: DNS2 Address"
416 type = 131
417
418
419class PPP_IPCP_Option_NBNS1(PPP_IPCP_Option_IPAddress):
420 name = "PPP IPCP Option: NBNS1 Address"
421 type = 130
422
423
424class PPP_IPCP_Option_NBNS2(PPP_IPCP_Option_IPAddress):
425 name = "PPP IPCP Option: NBNS2 Address"
426 type = 132
427
428
429class PPP_IPCP(Packet):
430 fields_desc = [
431 ByteEnumField("code", 1, _PPP_conftypes),
432 XByteField("id", 0),
433 FieldLenField("len", None, fmt="H", length_of="options",
434 adjust=lambda _, val: val + 4),
435 PacketListField("options", [], PPP_IPCP_Option,
436 length_from=lambda pkt: pkt.len - 4)
437 ]
438
439
440# ECP
441
442_PPP_ecpopttypes = {0: "OUI",
443 1: "DESE", }
444
445
446class PPP_ECP_Option(Packet):
447 name = "PPP ECP Option"
448 fields_desc = [
449 ByteEnumField("type", None, _PPP_ecpopttypes),
450 FieldLenField("len", None, length_of="data", fmt="B",
451 adjust=lambda _, val: val + 2),
452 StrLenField("data", "", length_from=lambda pkt: max(0, pkt.len - 2)),
453 ]
454
455 def extract_padding(self, pay):
456 return b"", pay
457
458 registered_options = {}
459
460 @classmethod
461 def register_variant(cls):
462 cls.registered_options[cls.type.default] = cls
463
464 @classmethod
465 def dispatch_hook(cls, _pkt=None, *args, **kargs):
466 if _pkt:
467 o = orb(_pkt[0])
468 return cls.registered_options.get(o, cls)
469 return cls
470
471
472class PPP_ECP_Option_OUI(PPP_ECP_Option):
473 fields_desc = [
474 ByteEnumField("type", 0, _PPP_ecpopttypes),
475 FieldLenField("len", None, length_of="data", fmt="B",
476 adjust=lambda _, val: val + 6),
477 OUIField("oui", 0),
478 ByteField("subtype", 0),
479 StrLenField("data", "", length_from=lambda pkt: pkt.len - 6),
480 ]
481
482
483class PPP_ECP(Packet):
484 fields_desc = [
485 ByteEnumField("code", 1, _PPP_conftypes),
486 XByteField("id", 0),
487 FieldLenField("len", None, fmt="H", length_of="options",
488 adjust=lambda _, val: val + 4),
489 PacketListField("options", [], PPP_ECP_Option,
490 length_from=lambda pkt: pkt.len - 4),
491 ]
492
493# Link Control Protocol (RFC 1661)
494
495
496_PPP_lcptypes = {1: "Configure-Request",
497 2: "Configure-Ack",
498 3: "Configure-Nak",
499 4: "Configure-Reject",
500 5: "Terminate-Request",
501 6: "Terminate-Ack",
502 7: "Code-Reject",
503 8: "Protocol-Reject",
504 9: "Echo-Request",
505 10: "Echo-Reply",
506 11: "Discard-Request"}
507
508
509class PPP_LCP(Packet):
510 name = "PPP Link Control Protocol"
511 fields_desc = [
512 ByteEnumField("code", 5, _PPP_lcptypes),
513 XByteField("id", 0),
514 FieldLenField("len", None, fmt="H", length_of="data",
515 adjust=lambda _, val: val + 4),
516 StrLenField("data", "", length_from=lambda pkt: pkt.len - 4),
517 ]
518
519 def mysummary(self):
520 return self.sprintf('LCP %code%')
521
522 def extract_padding(self, pay):
523 return b"", pay
524
525 @classmethod
526 def dispatch_hook(cls, _pkt=None, *args, **kargs):
527 if _pkt:
528 o = orb(_pkt[0])
529 if o in [1, 2, 3, 4]:
530 return PPP_LCP_Configure
531 elif o in [5, 6]:
532 return PPP_LCP_Terminate
533 elif o == 7:
534 return PPP_LCP_Code_Reject
535 elif o == 8:
536 return PPP_LCP_Protocol_Reject
537 elif o in [9, 10]:
538 return PPP_LCP_Echo
539 elif o == 11:
540 return PPP_LCP_Discard_Request
541 else:
542 return cls
543 return cls
544
545
546_PPP_lcp_optiontypes = {1: "Maximum-Receive-Unit",
547 2: "Async-Control-Character-Map",
548 3: "Authentication-protocol",
549 4: "Quality-protocol",
550 5: "Magic-number",
551 7: "Protocol-Field-Compression",
552 8: "Address-and-Control-Field-Compression",
553 13: "Callback"}
554
555
556class PPP_LCP_Option(Packet):
557 name = "PPP LCP Option"
558 fields_desc = [
559 ByteEnumField("type", None, _PPP_lcp_optiontypes),
560 FieldLenField("len", None, fmt="B", length_of="data",
561 adjust=lambda _, val: val + 2),
562 StrLenField("data", None, length_from=lambda pkt: pkt.len - 2),
563 ]
564
565 def extract_padding(self, pay):
566 return b"", pay
567
568 registered_options = {}
569
570 @classmethod
571 def register_variant(cls):
572 cls.registered_options[cls.type.default] = cls
573
574 @classmethod
575 def dispatch_hook(cls, _pkt=None, *args, **kargs):
576 if _pkt:
577 o = orb(_pkt[0])
578 return cls.registered_options.get(o, cls)
579 return cls
580
581
582class PPP_LCP_MRU_Option(PPP_LCP_Option):
583 fields_desc = [ByteEnumField("type", 1, _PPP_lcp_optiontypes),
584 ByteField("len", 4),
585 ShortField("max_recv_unit", 1500)]
586
587
588_PPP_LCP_auth_protocols = {
589 0xc023: "Password authentication protocol",
590 0xc223: "Challenge-response authentication protocol",
591 0xc227: "PPP Extensible authentication protocol",
592}
593
594_PPP_LCP_CHAP_algorithms = {
595 5: "MD5",
596 6: "SHA1",
597 128: "MS-CHAP",
598 129: "MS-CHAP-v2",
599}
600
601
602class PPP_LCP_ACCM_Option(PPP_LCP_Option):
603 fields_desc = [
604 ByteEnumField("type", 2, _PPP_lcp_optiontypes),
605 ByteField("len", 6),
606 BitField("accm", 0x00000000, 32),
607 ]
608
609
610def adjust_auth_len(pkt, x):
611 if pkt.auth_protocol == 0xc223:
612 return 5
613 elif pkt.auth_protocol == 0xc023:
614 return 4
615 else:
616 return x + 4
617
618
619class PPP_LCP_Auth_Protocol_Option(PPP_LCP_Option):
620 fields_desc = [
621 ByteEnumField("type", 3, _PPP_lcp_optiontypes),
622 FieldLenField("len", None, fmt="B", length_of="data",
623 adjust=adjust_auth_len),
624 ShortEnumField("auth_protocol", 0xc023, _PPP_LCP_auth_protocols),
625 ConditionalField(
626 StrLenField("data", '', length_from=lambda pkt: pkt.len - 4),
627 lambda pkt: pkt.auth_protocol != 0xc223
628 ),
629 ConditionalField(
630 ByteEnumField("algorithm", 5, _PPP_LCP_CHAP_algorithms),
631 lambda pkt: pkt.auth_protocol == 0xc223
632 ),
633 ]
634
635
636_PPP_LCP_quality_protocols = {0xc025: "Link Quality Report"}
637
638
639class PPP_LCP_Quality_Protocol_Option(PPP_LCP_Option):
640 fields_desc = [
641 ByteEnumField("type", 4, _PPP_lcp_optiontypes),
642 FieldLenField("len", None, fmt="B", length_of="data",
643 adjust=lambda _, val: val + 4),
644 ShortEnumField("quality_protocol", 0xc025, _PPP_LCP_quality_protocols),
645 StrLenField("data", "", length_from=lambda pkt: pkt.len - 4),
646 ]
647
648
649class PPP_LCP_Magic_Number_Option(PPP_LCP_Option):
650 fields_desc = [
651 ByteEnumField("type", 5, _PPP_lcp_optiontypes),
652 ByteField("len", 6),
653 IntField("magic_number", None),
654 ]
655
656
657_PPP_lcp_callback_operations = {
658 0: "Location determined by user authentication",
659 1: "Dialing string",
660 2: "Location identifier",
661 3: "E.164 number",
662 4: "Distinguished name",
663}
664
665
666class PPP_LCP_Callback_Option(PPP_LCP_Option):
667 fields_desc = [
668 ByteEnumField("type", 13, _PPP_lcp_optiontypes),
669 FieldLenField("len", None, fmt="B", length_of="message",
670 adjust=lambda _, val: val + 3),
671 ByteEnumField("operation", 0, _PPP_lcp_callback_operations),
672 StrLenField("message", "", length_from=lambda pkt: pkt.len - 3)
673 ]
674
675
676class PPP_LCP_Configure(PPP_LCP):
677 fields_desc = [
678 ByteEnumField("code", 1, _PPP_lcptypes),
679 XByteField("id", 0),
680 FieldLenField("len", None, fmt="H", length_of="options",
681 adjust=lambda _, val: val + 4),
682 PacketListField("options", [], PPP_LCP_Option,
683 length_from=lambda pkt: pkt.len - 4),
684 ]
685
686 def answers(self, other):
687 return (
688 isinstance(other, PPP_LCP_Configure) and self.code in [2, 3, 4] and
689 other.code == 1 and other.id == self.id
690 )
691
692
693class PPP_LCP_Terminate(PPP_LCP):
694
695 def answers(self, other):
696 return (
697 isinstance(other, PPP_LCP_Terminate) and self.code == 6 and
698 other.code == 5 and other.id == self.id
699 )
700
701
702class PPP_LCP_Code_Reject(PPP_LCP):
703 fields_desc = [
704 ByteEnumField("code", 7, _PPP_lcptypes),
705 XByteField("id", 0),
706 FieldLenField("len", None, fmt="H", length_of="rejected_packet",
707 adjust=lambda _, val: val + 4),
708 PacketField("rejected_packet", None, PPP_LCP),
709 ]
710
711
712class PPP_LCP_Protocol_Reject(PPP_LCP):
713 fields_desc = [
714 ByteEnumField("code", 8, _PPP_lcptypes),
715 XByteField("id", 0),
716 FieldLenField("len", None, fmt="H", length_of="rejected_information",
717 adjust=lambda _, val: val + 6),
718 ShortEnumField("rejected_protocol", None, _PPP_PROTOCOLS),
719 PacketField("rejected_information", None, Packet),
720 ]
721
722
723class PPP_LCP_Discard_Request(PPP_LCP):
724 fields_desc = [
725 ByteEnumField("code", 11, _PPP_lcptypes),
726 XByteField("id", 0),
727 FieldLenField("len", None, fmt="H", length_of="data",
728 adjust=lambda _, val: val + 8),
729 IntField("magic_number", None),
730 StrLenField("data", "", length_from=lambda pkt: pkt.len - 8),
731 ]
732
733
734class PPP_LCP_Echo(PPP_LCP_Discard_Request):
735 code = 9
736
737 def answers(self, other):
738 return (
739 isinstance(other, PPP_LCP_Echo) and self.code == 10 and
740 other.code == 9 and self.id == other.id
741 )
742
743
744# Password authentication protocol (RFC 1334)
745
746
747_PPP_paptypes = {1: "Authenticate-Request",
748 2: "Authenticate-Ack",
749 3: "Authenticate-Nak"}
750
751
752class PPP_PAP(Packet):
753 name = "PPP Password Authentication Protocol"
754 fields_desc = [
755 ByteEnumField("code", 1, _PPP_paptypes),
756 XByteField("id", 0),
757 FieldLenField("len", None, fmt="!H", length_of="data",
758 adjust=lambda _, val: val + 4),
759 StrLenField("data", "", length_from=lambda pkt: pkt.len - 4),
760 ]
761
762 @classmethod
763 def dispatch_hook(cls, _pkt=None, *_, **kargs):
764 code = None
765 if _pkt:
766 code = orb(_pkt[0])
767 elif "code" in kargs:
768 code = kargs["code"]
769 if isinstance(code, str):
770 code = cls.fields_desc[0].s2i[code]
771
772 if code == 1:
773 return PPP_PAP_Request
774 elif code in [2, 3]:
775 return PPP_PAP_Response
776 return cls
777
778 def extract_padding(self, pay):
779 return "", pay
780
781
782class PPP_PAP_Request(PPP_PAP):
783 fields_desc = [
784 ByteEnumField("code", 1, _PPP_paptypes),
785 XByteField("id", 0),
786 FieldLenField("len", None, fmt="!H", length_of="username",
787 adjust=lambda pkt, val: val + 6 + len(pkt.password)),
788 FieldLenField("username_len", None, fmt="B", length_of="username"),
789 StrLenField("username", None,
790 length_from=lambda pkt: pkt.username_len),
791 FieldLenField("passwd_len", None, fmt="B", length_of="password"),
792 StrLenField("password", None, length_from=lambda pkt: pkt.passwd_len),
793 ]
794
795 def mysummary(self):
796 return self.sprintf("PAP-Request username=%PPP_PAP_Request.username%"
797 " password=%PPP_PAP_Request.password%")
798
799
800class PPP_PAP_Response(PPP_PAP):
801 fields_desc = [
802 ByteEnumField("code", 2, _PPP_paptypes),
803 XByteField("id", 0),
804 FieldLenField("len", None, fmt="!H", length_of="message",
805 adjust=lambda _, val: val + 5),
806 FieldLenField("msg_len", None, fmt="B", length_of="message"),
807 StrLenField("message", "", length_from=lambda pkt: pkt.msg_len),
808 ]
809
810 def answers(self, other):
811 return isinstance(other, PPP_PAP_Request) and other.id == self.id
812
813 def mysummary(self):
814 res = "PAP-Ack" if self.code == 2 else "PAP-Nak"
815 if self.msg_len > 0:
816 res += self.sprintf(" msg=%PPP_PAP_Response.message%")
817 return res
818
819
820# Challenge Handshake Authentication protocol (RFC1994)
821
822_PPP_chaptypes = {1: "Challenge",
823 2: "Response",
824 3: "Success",
825 4: "Failure"}
826
827
828class PPP_CHAP(Packet):
829 name = "PPP Challenge Handshake Authentication Protocol"
830 fields_desc = [
831 ByteEnumField("code", 1, _PPP_chaptypes),
832 XByteField("id", 0),
833 FieldLenField("len", None, fmt="!H", length_of="data",
834 adjust=lambda _, val: val + 4),
835 StrLenField("data", "", length_from=lambda pkt: pkt.len - 4),
836 ]
837
838 def answers(self, other):
839 return isinstance(other, PPP_CHAP_ChallengeResponse) \
840 and other.code == 2 and self.code in (3, 4) \
841 and self.id == other.id
842
843 @classmethod
844 def dispatch_hook(cls, _pkt=None, *_, **kargs):
845 code = None
846 if _pkt:
847 code = orb(_pkt[0])
848 elif "code" in kargs:
849 code = kargs["code"]
850 if isinstance(code, str):
851 code = cls.fields_desc[0].s2i[code]
852
853 if code in (1, 2):
854 return PPP_CHAP_ChallengeResponse
855 return cls
856
857 def extract_padding(self, pay):
858 return "", pay
859
860 def mysummary(self):
861 if self.code == 3:
862 return self.sprintf("CHAP Success message=%PPP_CHAP.data%")
863 elif self.code == 4:
864 return self.sprintf("CHAP Failure message=%PPP_CHAP.data%")
865
866
867class PPP_CHAP_ChallengeResponse(PPP_CHAP):
868 fields_desc = [
869 ByteEnumField("code", 1, _PPP_chaptypes),
870 XByteField("id", 0),
871 FieldLenField(
872 "len", None, fmt="!H", length_of="value",
873 adjust=lambda pkt, val: val + len(pkt.optional_name) + 5,
874 ),
875 FieldLenField("value_size", None, fmt="B", length_of="value"),
876 XStrLenField("value", b'\x00\x00\x00\x00\x00\x00\x00\x00',
877 length_from=lambda pkt: pkt.value_size),
878 StrLenField("optional_name", "",
879 length_from=lambda pkt: pkt.len - pkt.value_size - 5),
880 ]
881
882 def answers(self, other):
883 return isinstance(other, PPP_CHAP_ChallengeResponse) \
884 and other.code == 1 and self.code == 2 and self.id == other.id
885
886 def mysummary(self):
887 if self.code == 1:
888 return self.sprintf(
889 "CHAP challenge=0x%PPP_CHAP_ChallengeResponse.value% "
890 "optional_name=%PPP_CHAP_ChallengeResponse.optional_name%"
891 )
892 elif self.code == 2:
893 return self.sprintf(
894 "CHAP response=0x%PPP_CHAP_ChallengeResponse.value% "
895 "optional_name=%PPP_CHAP_ChallengeResponse.optional_name%"
896 )
897 else:
898 return super(PPP_CHAP_ChallengeResponse, self).mysummary()
899
900
901bind_layers(PPPoED, PPPoED_Tags, type=1)
902bind_layers(Ether, PPPoED, type=0x8863)
903bind_layers(Ether, PPPoE, type=0x8864)
904bind_layers(CookedLinux, PPPoED, proto=0x8863)
905bind_layers(CookedLinux, PPPoE, proto=0x8864)
906bind_layers(PPPoE, PPP, code=0)
907bind_layers(HDLC, PPP,)
908bind_layers(DIR_PPP, PPP)
909bind_layers(PPP, EAP, proto=0xc227)
910bind_layers(PPP, IP, proto=0x0021)
911bind_layers(PPP, IPv6, proto=0x0057)
912bind_layers(PPP, PPP_CHAP, proto=0xc223)
913bind_layers(PPP, PPP_IPCP, proto=0x8021)
914bind_layers(PPP, PPP_ECP, proto=0x8053)
915bind_layers(PPP, PPP_LCP, proto=0xc021)
916bind_layers(PPP, PPP_PAP, proto=0xc023)
917bind_layers(Ether, PPP_IPCP, type=0x8021)
918bind_layers(Ether, PPP_ECP, type=0x8053)
919bind_layers(GRE_PPTP, PPP, proto=0x880b)
920
921
922conf.l2types.register(DLT_PPP, PPP)
923conf.l2types.register(DLT_PPP_SERIAL, HDLC)
924conf.l2types.register(DLT_PPP_ETHER, PPPoE)
925conf.l2types.register(DLT_PPP_WITH_DIR, DIR_PPP)