1## This file is part of Scapy 2## See http://www.secdev.org/projects/scapy for more informations 3## Copyright (C) Philippe Biondi <[email protected]> 4## This program is published under a GPLv2 license 5 6## Copyright (C) 2005 Guillaume Valadon <[email protected]> 7## Arnaud Ebalard <[email protected]> 8 9""" 10DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315] 11""" 12 13from __future__ import print_function 14import socket 15import struct 16import time 17 18from scapy.ansmachine import AnsweringMachine 19from scapy.arch import get_if_raw_hwaddr, in6_getifaddr 20from scapy.config import conf 21from scapy.data import EPOCH, ETHER_ANY 22from scapy.compat import * 23from scapy.error import warning 24from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \ 25 FlagsField, IntEnumField, IntField, MACField, PacketField, \ 26 PacketListField, ShortEnumField, ShortField, StrField, StrFixedLenField, \ 27 StrLenField, UTCTimeField, X3BytesField, XIntField, XShortEnumField, \ 28 PacketLenField 29from scapy.layers.inet import UDP 30from scapy.layers.inet6 import DomainNameListField, IP6Field, IP6ListField, IPv6 31from scapy.packet import Packet, bind_bottom_up 32from scapy.pton_ntop import inet_pton 33from scapy.sendrecv import send 34from scapy.themes import Color 35from scapy.utils6 import in6_addrtovendor, in6_islladdr 36 37############################################################################# 38# Helpers ## 39############################################################################# 40 41def get_cls(name, fallback_cls): 42 return globals().get(name, fallback_cls) 43 44 45############################################################################# 46############################################################################# 47### DHCPv6 ### 48############################################################################# 49############################################################################# 50 51All_DHCP_Relay_Agents_and_Servers = "ff02::1:2" 52All_DHCP_Servers = "ff05::1:3" # Site-Local scope : deprecated by 3879 53 54dhcp6opts = { 1: "CLIENTID", 55 2: "SERVERID", 56 3: "IA_NA", 57 4: "IA_TA", 58 5: "IAADDR", 59 6: "ORO", 60 7: "PREFERENCE", 61 8: "ELAPSED_TIME", 62 9: "RELAY_MSG", 63 11: "AUTH", 64 12: "UNICAST", 65 13: "STATUS_CODE", 66 14: "RAPID_COMMIT", 67 15: "USER_CLASS", 68 16: "VENDOR_CLASS", 69 17: "VENDOR_OPTS", 70 18: "INTERFACE_ID", 71 19: "RECONF_MSG", 72 20: "RECONF_ACCEPT", 73 21: "SIP Servers Domain Name List", #RFC3319 74 22: "SIP Servers IPv6 Address List", #RFC3319 75 23: "DNS Recursive Name Server Option", #RFC3646 76 24: "Domain Search List option", #RFC3646 77 25: "OPTION_IA_PD", #RFC3633 78 26: "OPTION_IAPREFIX", #RFC3633 79 27: "OPTION_NIS_SERVERS", #RFC3898 80 28: "OPTION_NISP_SERVERS", #RFC3898 81 29: "OPTION_NIS_DOMAIN_NAME", #RFC3898 82 30: "OPTION_NISP_DOMAIN_NAME", #RFC3898 83 31: "OPTION_SNTP_SERVERS", #RFC4075 84 32: "OPTION_INFORMATION_REFRESH_TIME", #RFC4242 85 33: "OPTION_BCMCS_SERVER_D", #RFC4280 86 34: "OPTION_BCMCS_SERVER_A", #RFC4280 87 36: "OPTION_GEOCONF_CIVIC", #RFC-ietf-geopriv-dhcp-civil-09.txt 88 37: "OPTION_REMOTE_ID", #RFC4649 89 38: "OPTION_SUBSCRIBER_ID", #RFC4580 90 39: "OPTION_CLIENT_FQDN", #RFC4704 91 68: "OPTION_VSS", #RFC6607 92 79: "OPTION_CLIENT_LINKLAYER_ADDR" } #RFC6939 93 94dhcp6opts_by_code = { 1: "DHCP6OptClientId", 95 2: "DHCP6OptServerId", 96 3: "DHCP6OptIA_NA", 97 4: "DHCP6OptIA_TA", 98 5: "DHCP6OptIAAddress", 99 6: "DHCP6OptOptReq", 100 7: "DHCP6OptPref", 101 8: "DHCP6OptElapsedTime", 102 9: "DHCP6OptRelayMsg", 103 11: "DHCP6OptAuth", 104 12: "DHCP6OptServerUnicast", 105 13: "DHCP6OptStatusCode", 106 14: "DHCP6OptRapidCommit", 107 15: "DHCP6OptUserClass", 108 16: "DHCP6OptVendorClass", 109 17: "DHCP6OptVendorSpecificInfo", 110 18: "DHCP6OptIfaceId", 111 19: "DHCP6OptReconfMsg", 112 20: "DHCP6OptReconfAccept", 113 21: "DHCP6OptSIPDomains", #RFC3319 114 22: "DHCP6OptSIPServers", #RFC3319 115 23: "DHCP6OptDNSServers", #RFC3646 116 24: "DHCP6OptDNSDomains", #RFC3646 117 25: "DHCP6OptIA_PD", #RFC3633 118 26: "DHCP6OptIAPrefix", #RFC3633 119 27: "DHCP6OptNISServers", #RFC3898 120 28: "DHCP6OptNISPServers", #RFC3898 121 29: "DHCP6OptNISDomain", #RFC3898 122 30: "DHCP6OptNISPDomain", #RFC3898 123 31: "DHCP6OptSNTPServers", #RFC4075 124 32: "DHCP6OptInfoRefreshTime", #RFC4242 125 33: "DHCP6OptBCMCSDomains", #RFC4280 126 34: "DHCP6OptBCMCSServers", #RFC4280 127 #36: "DHCP6OptGeoConf", #RFC-ietf-geopriv-dhcp-civil-09.txt 128 37: "DHCP6OptRemoteID", #RFC4649 129 38: "DHCP6OptSubscriberID", #RFC4580 130 39: "DHCP6OptClientFQDN", #RFC4704 131 #40: "DHCP6OptPANAAgent", #RFC-ietf-dhc-paa-option-05.txt 132 #41: "DHCP6OptNewPOSIXTimeZone, #RFC4833 133 #42: "DHCP6OptNewTZDBTimeZone, #RFC4833 134 43: "DHCP6OptRelayAgentERO", #RFC4994 135 #44: "DHCP6OptLQQuery", #RFC5007 136 #45: "DHCP6OptLQClientData", #RFC5007 137 #46: "DHCP6OptLQClientTime", #RFC5007 138 #47: "DHCP6OptLQRelayData", #RFC5007 139 #48: "DHCP6OptLQClientLink", #RFC5007 140 68: "DHCP6OptVSS", #RFC6607 141 79: "DHCP6OptClientLinkLayerAddr", #RFC6939 142} 143 144 145# sect 5.3 RFC 3315 : DHCP6 Messages types 146dhcp6types = { 1:"SOLICIT", 147 2:"ADVERTISE", 148 3:"REQUEST", 149 4:"CONFIRM", 150 5:"RENEW", 151 6:"REBIND", 152 7:"REPLY", 153 8:"RELEASE", 154 9:"DECLINE", 155 10:"RECONFIGURE", 156 11:"INFORMATION-REQUEST", 157 12:"RELAY-FORW", 158 13:"RELAY-REPL" } 159 160 161##################################################################### 162### DHCPv6 DUID related stuff ### 163##################################################################### 164 165duidtypes = { 1: "Link-layer address plus time", 166 2: "Vendor-assigned unique ID based on Enterprise Number", 167 3: "Link-layer Address", 168 4: "UUID" } 169 170# DUID hardware types - RFC 826 - Extracted from 171# http://www.iana.org/assignments/arp-parameters on 31/10/06 172# We should add the length of every kind of address. 173duidhwtypes = { 0: "NET/ROM pseudo", # Not referenced by IANA 174 1: "Ethernet (10Mb)", 175 2: "Experimental Ethernet (3Mb)", 176 3: "Amateur Radio AX.25", 177 4: "Proteon ProNET Token Ring", 178 5: "Chaos", 179 6: "IEEE 802 Networks", 180 7: "ARCNET", 181 8: "Hyperchannel", 182 9: "Lanstar", 183 10: "Autonet Short Address", 184 11: "LocalTalk", 185 12: "LocalNet (IBM PCNet or SYTEK LocalNET)", 186 13: "Ultra link", 187 14: "SMDS", 188 15: "Frame Relay", 189 16: "Asynchronous Transmission Mode (ATM)", 190 17: "HDLC", 191 18: "Fibre Channel", 192 19: "Asynchronous Transmission Mode (ATM)", 193 20: "Serial Line", 194 21: "Asynchronous Transmission Mode (ATM)", 195 22: "MIL-STD-188-220", 196 23: "Metricom", 197 24: "IEEE 1394.1995", 198 25: "MAPOS", 199 26: "Twinaxial", 200 27: "EUI-64", 201 28: "HIPARP", 202 29: "IP and ARP over ISO 7816-3", 203 30: "ARPSec", 204 31: "IPsec tunnel", 205 32: "InfiniBand (TM)", 206 33: "TIA-102 Project 25 Common Air Interface (CAI)" } 207 208class _UTCTimeField(UTCTimeField): 209 def __init__(self, *args, **kargs): 210 epoch_2000 = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch 211 UTCTimeField.__init__(self, epoch=epoch_2000, *args, **kargs) 212 213class _LLAddrField(MACField): 214 pass 215 216# XXX We only support Ethernet addresses at the moment. _LLAddrField 217# will be modified when needed. Ask us. --arno 218class DUID_LLT(Packet): # sect 9.2 RFC 3315 219 name = "DUID - Link-layer address plus time" 220 fields_desc = [ ShortEnumField("type", 1, duidtypes), 221 XShortEnumField("hwtype", 1, duidhwtypes), 222 _UTCTimeField("timeval", 0), # i.e. 01 Jan 2000 223 _LLAddrField("lladdr", ETHER_ANY) ] 224 225# In fact, IANA enterprise-numbers file available at 226# http://www.iana.org/assignments/enterprise-numbers 227# is simply huge (more than 2Mo and 600Ko in bz2). I'll 228# add only most common vendors, and encountered values. 229# -- arno 230iana_enterprise_num = { 9: "ciscoSystems", 231 35: "Nortel Networks", 232 43: "3Com", 233 311: "Microsoft", 234 2636: "Juniper Networks, Inc.", 235 4526: "Netgear", 236 5771: "Cisco Systems, Inc.", 237 5842: "Cisco Systems", 238 16885: "Nortel Networks" } 239 240class DUID_EN(Packet): # sect 9.3 RFC 3315 241 name = "DUID - Assigned by Vendor Based on Enterprise Number" 242 fields_desc = [ ShortEnumField("type", 2, duidtypes), 243 IntEnumField("enterprisenum", 311, iana_enterprise_num), 244 StrField("id","") ] 245 246class DUID_LL(Packet): # sect 9.4 RFC 3315 247 name = "DUID - Based on Link-layer Address" 248 fields_desc = [ ShortEnumField("type", 3, duidtypes), 249 XShortEnumField("hwtype", 1, duidhwtypes), 250 _LLAddrField("lladdr", ETHER_ANY) ] 251 252class DUID_UUID(Packet): # RFC 6355 253 name = "DUID - Based on UUID" 254 fields_desc = [ ShortEnumField("type", 4, duidtypes), 255 StrFixedLenField("uuid","", 16) ] 256 257duid_cls = { 1: "DUID_LLT", 258 2: "DUID_EN", 259 3: "DUID_LL", 260 4: "DUID_UUID"} 261 262##################################################################### 263### DHCPv6 Options classes ### 264##################################################################### 265 266class _DHCP6OptGuessPayload(Packet): 267 def guess_payload_class(self, payload): 268 cls = conf.raw_layer 269 if len(payload) > 2 : 270 opt = struct.unpack("!H", payload[:2])[0] 271 cls = get_cls(dhcp6opts_by_code.get(opt, "DHCP6OptUnknown"), DHCP6OptUnknown) 272 return cls 273 274class DHCP6OptUnknown(_DHCP6OptGuessPayload): # A generic DHCPv6 Option 275 name = "Unknown DHCPv6 Option" 276 fields_desc = [ ShortEnumField("optcode", 0, dhcp6opts), 277 FieldLenField("optlen", None, length_of="data", fmt="!H"), 278 StrLenField("data", "", 279 length_from = lambda pkt: pkt.optlen)] 280 281class _DUIDField(PacketField): 282 __slots__ = ["length_from"] 283 def __init__(self, name, default, length_from=None): 284 StrField.__init__(self, name, default) 285 self.length_from = length_from 286 287 def i2m(self, pkt, i): 288 return raw(i) 289 290 def m2i(self, pkt, x): 291 cls = conf.raw_layer 292 if len(x) > 4: 293 o = struct.unpack("!H", x[:2])[0] 294 cls = get_cls(duid_cls.get(o, conf.raw_layer), conf.raw_layer) 295 return cls(x) 296 297 def getfield(self, pkt, s): 298 l = self.length_from(pkt) 299 return s[l:], self.m2i(pkt,s[:l]) 300 301 302class DHCP6OptClientId(_DHCP6OptGuessPayload): # RFC sect 22.2 303 name = "DHCP6 Client Identifier Option" 304 fields_desc = [ ShortEnumField("optcode", 1, dhcp6opts), 305 FieldLenField("optlen", None, length_of="duid", fmt="!H"), 306 _DUIDField("duid", "", 307 length_from = lambda pkt: pkt.optlen) ] 308 309 310class DHCP6OptServerId(DHCP6OptClientId): # RFC sect 22.3 311 name = "DHCP6 Server Identifier Option" 312 optcode = 2 313 314# Should be encapsulated in the option field of IA_NA or IA_TA options 315# Can only appear at that location. 316# TODO : last field IAaddr-options is not defined in the reference document 317class DHCP6OptIAAddress(_DHCP6OptGuessPayload): # RFC sect 22.6 318 name = "DHCP6 IA Address Option (IA_TA or IA_NA suboption)" 319 fields_desc = [ ShortEnumField("optcode", 5, dhcp6opts), 320 FieldLenField("optlen", None, length_of="iaaddropts", 321 fmt="!H", adjust = lambda pkt,x: x+24), 322 IP6Field("addr", "::"), 323 IntField("preflft", 0), 324 IntField("validlft", 0), 325 StrLenField("iaaddropts", "", 326 length_from = lambda pkt: pkt.optlen - 24) ] 327 def guess_payload_class(self, payload): 328 return conf.padding_layer 329 330class _IANAOptField(PacketListField): 331 def i2len(self, pkt, z): 332 if z is None or z == []: 333 return 0 334 return sum(len(raw(x)) for x in z) 335 336 def getfield(self, pkt, s): 337 l = self.length_from(pkt) 338 lst = [] 339 remain, payl = s[:l], s[l:] 340 while len(remain)>0: 341 p = self.m2i(pkt,remain) 342 if conf.padding_layer in p: 343 pad = p[conf.padding_layer] 344 remain = pad.load 345 del(pad.underlayer.payload) 346 else: 347 remain = "" 348 lst.append(p) 349 return payl,lst 350 351class DHCP6OptIA_NA(_DHCP6OptGuessPayload): # RFC sect 22.4 352 name = "DHCP6 Identity Association for Non-temporary Addresses Option" 353 fields_desc = [ ShortEnumField("optcode", 3, dhcp6opts), 354 FieldLenField("optlen", None, length_of="ianaopts", 355 fmt="!H", adjust = lambda pkt,x: x+12), 356 XIntField("iaid", None), 357 IntField("T1", None), 358 IntField("T2", None), 359 _IANAOptField("ianaopts", [], DHCP6OptIAAddress, 360 length_from = lambda pkt: pkt.optlen-12) ] 361 362class _IATAOptField(_IANAOptField): 363 pass 364 365class DHCP6OptIA_TA(_DHCP6OptGuessPayload): # RFC sect 22.5 366 name = "DHCP6 Identity Association for Temporary Addresses Option" 367 fields_desc = [ ShortEnumField("optcode", 4, dhcp6opts), 368 FieldLenField("optlen", None, length_of="iataopts", 369 fmt="!H", adjust = lambda pkt,x: x+4), 370 XIntField("iaid", None), 371 _IATAOptField("iataopts", [], DHCP6OptIAAddress, 372 length_from = lambda pkt: pkt.optlen-4) ] 373 374 375#### DHCPv6 Option Request Option ################################### 376 377class _OptReqListField(StrLenField): 378 islist = 1 379 def i2h(self, pkt, x): 380 if x is None: 381 return [] 382 return x 383 384 def i2len(self, pkt, x): 385 return 2*len(x) 386 387 def any2i(self, pkt, x): 388 return x 389 390 def i2repr(self, pkt, x): 391 s = [] 392 for y in self.i2h(pkt, x): 393 if y in dhcp6opts: 394 s.append(dhcp6opts[y]) 395 else: 396 s.append("%d" % y) 397 return "[%s]" % ", ".join(s) 398 399 def m2i(self, pkt, x): 400 r = [] 401 while len(x) != 0: 402 if len(x)<2: 403 warning("Odd length for requested option field. Rejecting last byte") 404 return r 405 r.append(struct.unpack("!H", x[:2])[0]) 406 x = x[2:] 407 return r 408 409 def i2m(self, pkt, x): 410 return b"".join(struct.pack('!H', y) for y in x) 411 412# A client may include an ORO in a solicit, Request, Renew, Rebind, 413# Confirm or Information-request 414class DHCP6OptOptReq(_DHCP6OptGuessPayload): # RFC sect 22.7 415 name = "DHCP6 Option Request Option" 416 fields_desc = [ ShortEnumField("optcode", 6, dhcp6opts), 417 FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), 418 _OptReqListField("reqopts", [23, 24], 419 length_from = lambda pkt: pkt.optlen) ] 420 421 422#### DHCPv6 Preference Option ####################################### 423 424# emise par un serveur pour affecter le choix fait par le client. Dans 425# les messages Advertise, a priori 426class DHCP6OptPref(_DHCP6OptGuessPayload): # RFC sect 22.8 427 name = "DHCP6 Preference Option" 428 fields_desc = [ ShortEnumField("optcode", 7, dhcp6opts), 429 ShortField("optlen", 1 ), 430 ByteField("prefval",255) ] 431 432 433#### DHCPv6 Elapsed Time Option ##################################### 434 435class _ElapsedTimeField(ShortField): 436 def i2repr(self, pkt, x): 437 if x == 0xffff: 438 return "infinity (0xffff)" 439 return "%.2f sec" % (self.i2h(pkt, x)/100.) 440 441class DHCP6OptElapsedTime(_DHCP6OptGuessPayload):# RFC sect 22.9 442 name = "DHCP6 Elapsed Time Option" 443 fields_desc = [ ShortEnumField("optcode", 8, dhcp6opts), 444 ShortField("optlen", 2), 445 _ElapsedTimeField("elapsedtime", 0) ] 446 447 448#### DHCPv6 Authentication Option ################################### 449 450# The following fields are set in an Authentication option for the 451# Reconfigure Key Authentication Protocol: 452# 453# protocol 3 454# 455# algorithm 1 456# 457# RDM 0 458# 459# The format of the Authentication information for the Reconfigure Key 460# Authentication Protocol is: 461# 462# 0 1 2 3 463# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 464# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 465# | Type | Value (128 bits) | 466# +-+-+-+-+-+-+-+-+ | 467# . . 468# . . 469# . +-+-+-+-+-+-+-+-+ 470# | | 471# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 472# 473# Type Type of data in Value field carried in this option: 474# 475# 1 Reconfigure Key value (used in Reply message). 476# 477# 2 HMAC-MD5 digest of the message (used in Reconfigure 478# message). 479# 480# Value Data as defined by field. 481 482 483# TODO : Decoding only at the moment 484class DHCP6OptAuth(_DHCP6OptGuessPayload): # RFC sect 22.11 485 name = "DHCP6 Option - Authentication" 486 fields_desc = [ ShortEnumField("optcode", 11, dhcp6opts), 487 FieldLenField("optlen", None, length_of="authinfo", 488 adjust = lambda pkt,x: x+11), 489 ByteField("proto", 3), # TODO : XXX 490 ByteField("alg", 1), # TODO : XXX 491 ByteField("rdm", 0), # TODO : XXX 492 StrFixedLenField("replay", "A"*8, 8), # TODO: XXX 493 StrLenField("authinfo", "", 494 length_from = lambda pkt: pkt.optlen - 11) ] 495 496#### DHCPv6 Server Unicast Option ################################### 497 498class _SrvAddrField(IP6Field): 499 def i2h(self, pkt, x): 500 if x is None: 501 return "::" 502 return x 503 504 def i2m(self, pkt, x): 505 return inet_pton(socket.AF_INET6, self.i2h(pkt,x)) 506 507class DHCP6OptServerUnicast(_DHCP6OptGuessPayload):# RFC sect 22.12 508 name = "DHCP6 Server Unicast Option" 509 fields_desc = [ ShortEnumField("optcode", 12, dhcp6opts), 510 ShortField("optlen", 16 ), 511 _SrvAddrField("srvaddr",None) ] 512 513 514#### DHCPv6 Status Code Option ###################################### 515 516dhcp6statuscodes = { 0:"Success", # sect 24.4 517 1:"UnspecFail", 518 2:"NoAddrsAvail", 519 3:"NoBinding", 520 4:"NotOnLink", 521 5:"UseMulticast", 522 6:"NoPrefixAvail"} # From RFC3633 523 524class DHCP6OptStatusCode(_DHCP6OptGuessPayload):# RFC sect 22.13 525 name = "DHCP6 Status Code Option" 526 fields_desc = [ ShortEnumField("optcode", 13, dhcp6opts), 527 FieldLenField("optlen", None, length_of="statusmsg", 528 fmt="!H", adjust = lambda pkt,x:x+2), 529 ShortEnumField("statuscode",None,dhcp6statuscodes), 530 StrLenField("statusmsg", "", 531 length_from = lambda pkt: pkt.optlen-2) ] 532 533 534#### DHCPv6 Rapid Commit Option ##################################### 535 536class DHCP6OptRapidCommit(_DHCP6OptGuessPayload): # RFC sect 22.14 537 name = "DHCP6 Rapid Commit Option" 538 fields_desc = [ ShortEnumField("optcode", 14, dhcp6opts), 539 ShortField("optlen", 0)] 540 541 542#### DHCPv6 User Class Option ####################################### 543 544class _UserClassDataField(PacketListField): 545 def i2len(self, pkt, z): 546 if z is None or z == []: 547 return 0 548 return sum(len(raw(x)) for x in z) 549 550 def getfield(self, pkt, s): 551 l = self.length_from(pkt) 552 lst = [] 553 remain, payl = s[:l], s[l:] 554 while len(remain)>0: 555 p = self.m2i(pkt,remain) 556 if conf.padding_layer in p: 557 pad = p[conf.padding_layer] 558 remain = pad.load 559 del(pad.underlayer.payload) 560 else: 561 remain = "" 562 lst.append(p) 563 return payl,lst 564 565 566class USER_CLASS_DATA(Packet): 567 name = "user class data" 568 fields_desc = [ FieldLenField("len", None, length_of="data"), 569 StrLenField("data", "", 570 length_from = lambda pkt: pkt.len) ] 571 def guess_payload_class(self, payload): 572 return conf.padding_layer 573 574class DHCP6OptUserClass(_DHCP6OptGuessPayload):# RFC sect 22.15 575 name = "DHCP6 User Class Option" 576 fields_desc = [ ShortEnumField("optcode", 15, dhcp6opts), 577 FieldLenField("optlen", None, fmt="!H", 578 length_of="userclassdata"), 579 _UserClassDataField("userclassdata", [], USER_CLASS_DATA, 580 length_from = lambda pkt: pkt.optlen) ] 581 582 583#### DHCPv6 Vendor Class Option ##################################### 584 585class _VendorClassDataField(_UserClassDataField): 586 pass 587 588class VENDOR_CLASS_DATA(USER_CLASS_DATA): 589 name = "vendor class data" 590 591class DHCP6OptVendorClass(_DHCP6OptGuessPayload):# RFC sect 22.16 592 name = "DHCP6 Vendor Class Option" 593 fields_desc = [ ShortEnumField("optcode", 16, dhcp6opts), 594 FieldLenField("optlen", None, length_of="vcdata", fmt="!H", 595 adjust = lambda pkt,x: x+4), 596 IntEnumField("enterprisenum",None , iana_enterprise_num ), 597 _VendorClassDataField("vcdata", [], VENDOR_CLASS_DATA, 598 length_from = lambda pkt: pkt.optlen-4) ] 599 600#### DHCPv6 Vendor-Specific Information Option ###################### 601 602class VENDOR_SPECIFIC_OPTION(_DHCP6OptGuessPayload): 603 name = "vendor specific option data" 604 fields_desc = [ ShortField("optcode", None), 605 FieldLenField("optlen", None, length_of="optdata"), 606 StrLenField("optdata", "", 607 length_from = lambda pkt: pkt.optlen) ] 608 def guess_payload_class(self, payload): 609 return conf.padding_layer 610 611# The third one that will be used for nothing interesting 612class DHCP6OptVendorSpecificInfo(_DHCP6OptGuessPayload):# RFC sect 22.17 613 name = "DHCP6 Vendor-specific Information Option" 614 fields_desc = [ ShortEnumField("optcode", 17, dhcp6opts), 615 FieldLenField("optlen", None, length_of="vso", fmt="!H", 616 adjust = lambda pkt,x: x+4), 617 IntEnumField("enterprisenum",None , iana_enterprise_num), 618 _VendorClassDataField("vso", [], VENDOR_SPECIFIC_OPTION, 619 length_from = lambda pkt: pkt.optlen-4) ] 620 621#### DHCPv6 Interface-ID Option ##################################### 622 623# Repasser sur cette option a la fin. Elle a pas l'air d'etre des 624# masses critique. 625class DHCP6OptIfaceId(_DHCP6OptGuessPayload):# RFC sect 22.18 626 name = "DHCP6 Interface-Id Option" 627 fields_desc = [ ShortEnumField("optcode", 18, dhcp6opts), 628 FieldLenField("optlen", None, fmt="!H", 629 length_of="ifaceid"), 630 StrLenField("ifaceid", "", 631 length_from = lambda pkt: pkt.optlen) ] 632 633 634#### DHCPv6 Reconfigure Message Option ############################## 635 636# A server includes a Reconfigure Message option in a Reconfigure 637# message to indicate to the client whether the client responds with a 638# renew message or an Information-request message. 639class DHCP6OptReconfMsg(_DHCP6OptGuessPayload): # RFC sect 22.19 640 name = "DHCP6 Reconfigure Message Option" 641 fields_desc = [ ShortEnumField("optcode", 19, dhcp6opts), 642 ShortField("optlen", 1 ), 643 ByteEnumField("msgtype", 11, { 5:"Renew Message", 644 11:"Information Request"}) ] 645 646 647#### DHCPv6 Reconfigure Accept Option ############################### 648 649# A client uses the Reconfigure Accept option to announce to the 650# server whether the client is willing to accept Recoonfigure 651# messages, and a server uses this option to tell the client whether 652# or not to accept Reconfigure messages. The default behavior in the 653# absence of this option, means unwillingness to accept reconfigure 654# messages, or instruction not to accept Reconfigure messages, for the 655# client and server messages, respectively. 656class DHCP6OptReconfAccept(_DHCP6OptGuessPayload): # RFC sect 22.20 657 name = "DHCP6 Reconfigure Accept Option" 658 fields_desc = [ ShortEnumField("optcode", 20, dhcp6opts), 659 ShortField("optlen", 0)] 660 661class DHCP6OptSIPDomains(_DHCP6OptGuessPayload): #RFC3319 662 name = "DHCP6 Option - SIP Servers Domain Name List" 663 fields_desc = [ ShortEnumField("optcode", 21, dhcp6opts), 664 FieldLenField("optlen", None, length_of="sipdomains"), 665 DomainNameListField("sipdomains", [], 666 length_from = lambda pkt: pkt.optlen) ] 667 668class DHCP6OptSIPServers(_DHCP6OptGuessPayload): #RFC3319 669 name = "DHCP6 Option - SIP Servers IPv6 Address List" 670 fields_desc = [ ShortEnumField("optcode", 22, dhcp6opts), 671 FieldLenField("optlen", None, length_of="sipservers"), 672 IP6ListField("sipservers", [], 673 length_from = lambda pkt: pkt.optlen) ] 674 675class DHCP6OptDNSServers(_DHCP6OptGuessPayload): #RFC3646 676 name = "DHCP6 Option - DNS Recursive Name Server" 677 fields_desc = [ ShortEnumField("optcode", 23, dhcp6opts), 678 FieldLenField("optlen", None, length_of="dnsservers"), 679 IP6ListField("dnsservers", [], 680 length_from = lambda pkt: pkt.optlen) ] 681 682class DHCP6OptDNSDomains(_DHCP6OptGuessPayload): #RFC3646 683 name = "DHCP6 Option - Domain Search List option" 684 fields_desc = [ ShortEnumField("optcode", 24, dhcp6opts), 685 FieldLenField("optlen", None, length_of="dnsdomains"), 686 DomainNameListField("dnsdomains", [], 687 length_from = lambda pkt: pkt.optlen) ] 688 689# TODO: Implement iaprefopts correctly when provided with more 690# information about it. 691class DHCP6OptIAPrefix(_DHCP6OptGuessPayload): #RFC3633 692 name = "DHCP6 Option - IA_PD Prefix option" 693 fields_desc = [ ShortEnumField("optcode", 26, dhcp6opts), 694 FieldLenField("optlen", None, length_of="iaprefopts", 695 adjust = lambda pkt,x: x+25), 696 IntField("preflft", 0), 697 IntField("validlft", 0), 698 ByteField("plen", 48), # TODO: Challenge that default value 699 IP6Field("prefix", "2001:db8::"), # At least, global and won't hurt 700 StrLenField("iaprefopts", "", 701 length_from = lambda pkt: pkt.optlen-25) ] 702 703class DHCP6OptIA_PD(_DHCP6OptGuessPayload): #RFC3633 704 name = "DHCP6 Option - Identity Association for Prefix Delegation" 705 fields_desc = [ ShortEnumField("optcode", 25, dhcp6opts), 706 FieldLenField("optlen", None, length_of="iapdopt", 707 adjust = lambda pkt,x: x+12), 708 IntField("iaid", 0), 709 IntField("T1", 0), 710 IntField("T2", 0), 711 PacketListField("iapdopt", [], DHCP6OptIAPrefix, 712 length_from = lambda pkt: pkt.optlen-12) ] 713 714class DHCP6OptNISServers(_DHCP6OptGuessPayload): #RFC3898 715 name = "DHCP6 Option - NIS Servers" 716 fields_desc = [ ShortEnumField("optcode", 27, dhcp6opts), 717 FieldLenField("optlen", None, length_of="nisservers"), 718 IP6ListField("nisservers", [], 719 length_from = lambda pkt: pkt.optlen) ] 720 721class DHCP6OptNISPServers(_DHCP6OptGuessPayload): #RFC3898 722 name = "DHCP6 Option - NIS+ Servers" 723 fields_desc = [ ShortEnumField("optcode", 28, dhcp6opts), 724 FieldLenField("optlen", None, length_of="nispservers"), 725 IP6ListField("nispservers", [], 726 length_from = lambda pkt: pkt.optlen) ] 727 728class DomainNameField(StrLenField): 729 def getfield(self, pkt, s): 730 l = self.length_from(pkt) 731 return s[l:], self.m2i(pkt,s[:l]) 732 733 def i2len(self, pkt, x): 734 return len(self.i2m(pkt, x)) 735 736 def m2i(self, pkt, x): 737 cur = [] 738 while x: 739 l = orb(x[0]) 740 cur.append(x[1:1+l]) 741 x = x[l+1:] 742 return b".".join(cur) 743 744 def i2m(self, pkt, x): 745 if not x: 746 return b"" 747 return b"".join(chb(len(z)) + z for z in x.split(b'.')) 748 749class DHCP6OptNISDomain(_DHCP6OptGuessPayload): #RFC3898 750 name = "DHCP6 Option - NIS Domain Name" 751 fields_desc = [ ShortEnumField("optcode", 29, dhcp6opts), 752 FieldLenField("optlen", None, length_of="nisdomain"), 753 DomainNameField("nisdomain", "", 754 length_from = lambda pkt: pkt.optlen) ] 755 756class DHCP6OptNISPDomain(_DHCP6OptGuessPayload): #RFC3898 757 name = "DHCP6 Option - NIS+ Domain Name" 758 fields_desc = [ ShortEnumField("optcode", 30, dhcp6opts), 759 FieldLenField("optlen", None, length_of="nispdomain"), 760 DomainNameField("nispdomain", "", 761 length_from= lambda pkt: pkt.optlen) ] 762 763class DHCP6OptSNTPServers(_DHCP6OptGuessPayload): #RFC4075 764 name = "DHCP6 option - SNTP Servers" 765 fields_desc = [ ShortEnumField("optcode", 31, dhcp6opts), 766 FieldLenField("optlen", None, length_of="sntpservers"), 767 IP6ListField("sntpservers", [], 768 length_from = lambda pkt: pkt.optlen) ] 769 770IRT_DEFAULT=86400 771IRT_MINIMUM=600 772class DHCP6OptInfoRefreshTime(_DHCP6OptGuessPayload): #RFC4242 773 name = "DHCP6 Option - Information Refresh Time" 774 fields_desc = [ ShortEnumField("optcode", 32, dhcp6opts), 775 ShortField("optlen", 4), 776 IntField("reftime", IRT_DEFAULT)] # One day 777 778class DHCP6OptBCMCSDomains(_DHCP6OptGuessPayload): #RFC4280 779 name = "DHCP6 Option - BCMCS Domain Name List" 780 fields_desc = [ ShortEnumField("optcode", 33, dhcp6opts), 781 FieldLenField("optlen", None, length_of="bcmcsdomains"), 782 DomainNameListField("bcmcsdomains", [], 783 length_from = lambda pkt: pkt.optlen) ] 784 785class DHCP6OptBCMCSServers(_DHCP6OptGuessPayload): #RFC4280 786 name = "DHCP6 Option - BCMCS Addresses List" 787 fields_desc = [ ShortEnumField("optcode", 34, dhcp6opts), 788 FieldLenField("optlen", None, length_of="bcmcsservers"), 789 IP6ListField("bcmcsservers", [], 790 length_from= lambda pkt: pkt.optlen) ] 791 792# TODO : Does Nothing at the moment 793class DHCP6OptGeoConf(_DHCP6OptGuessPayload): #RFC-ietf-geopriv-dhcp-civil-09.txt 794 name = "" 795 fields_desc = [ ShortEnumField("optcode", 36, dhcp6opts), 796 FieldLenField("optlen", None, length_of="optdata"), 797 StrLenField("optdata", "", 798 length_from = lambda pkt: pkt.optlen) ] 799 800# TODO: see if we encounter opaque values from vendor devices 801class DHCP6OptRemoteID(_DHCP6OptGuessPayload): #RFC4649 802 name = "DHCP6 Option - Relay Agent Remote-ID" 803 fields_desc = [ ShortEnumField("optcode", 37, dhcp6opts), 804 FieldLenField("optlen", None, length_of="remoteid", 805 adjust = lambda pkt,x: x+4), 806 IntEnumField("enterprisenum", None, iana_enterprise_num), 807 StrLenField("remoteid", "", 808 length_from = lambda pkt: pkt.optlen-4) ] 809 810# TODO : 'subscriberid' default value should be at least 1 byte long 811class DHCP6OptSubscriberID(_DHCP6OptGuessPayload): #RFC4580 812 name = "DHCP6 Option - Subscriber ID" 813 fields_desc = [ ShortEnumField("optcode", 38, dhcp6opts), 814 FieldLenField("optlen", None, length_of="subscriberid"), 815 StrLenField("subscriberid", "", 816 length_from = lambda pkt: pkt.optlen) ] 817 818# TODO : "The data in the Domain Name field MUST be encoded 819# as described in Section 8 of [5]" 820class DHCP6OptClientFQDN(_DHCP6OptGuessPayload): #RFC4704 821 name = "DHCP6 Option - Client FQDN" 822 fields_desc = [ ShortEnumField("optcode", 39, dhcp6opts), 823 FieldLenField("optlen", None, length_of="fqdn", 824 adjust = lambda pkt,x: x+1), 825 BitField("res", 0, 5), 826 FlagsField("flags", 0, 3, "SON" ), 827 DomainNameField("fqdn", "", 828 length_from = lambda pkt: pkt.optlen-1) ] 829 830class DHCP6OptRelayAgentERO(_DHCP6OptGuessPayload): # RFC4994 831 name = "DHCP6 Option - RelayRequest Option" 832 fields_desc = [ ShortEnumField("optcode", 43, dhcp6opts), 833 FieldLenField("optlen", None, length_of="reqopts", fmt="!H"), 834 _OptReqListField("reqopts", [23, 24], 835 length_from = lambda pkt: pkt.optlen) ] 836 837# "Client link-layer address type. The link-layer type MUST be a valid hardware 838# type assigned by the IANA, as described in [RFC0826] 839class DHCP6OptClientLinkLayerAddr(_DHCP6OptGuessPayload): # RFC6939 840 name = "DHCP6 Option - Client Link Layer address" 841 fields_desc = [ ShortEnumField("optcode", 79, dhcp6opts), 842 FieldLenField("optlen", None, length_of="clladdr", 843 adjust = lambda pkt,x: x+1), 844 ShortField("lltype", 1), # ethernet 845 _LLAddrField("clladdr", ETHER_ANY) ] 846 847# Virtual Subnet selection 848class DHCP6OptVSS(_DHCP6OptGuessPayload): # RFC6607 849 name = "DHCP6 Option - Virtual Subnet Selection" 850 fields_desc = [ ShortEnumField("optcode", 68, dhcp6opts), 851 FieldLenField("optlen", None, length_of="data", 852 adjust = lambda pkt,x: x+1), 853 ByteField("type", 255), # Default Global/default table 854 StrLenField("data", "", 855 length_from = lambda pkt: pkt.optlen) ] 856 857 858##################################################################### 859### DHCPv6 messages ### 860##################################################################### 861 862# Some state parameters of the protocols that should probably be 863# useful to have in the configuration (and keep up-to-date) 864DHCP6RelayAgentUnicastAddr="" 865DHCP6RelayHopCount="" 866DHCP6ServerUnicastAddr="" 867DHCP6ClientUnicastAddr="" 868DHCP6ClientIA_TA="" 869DHCP6ClientIA_NA="" 870DHCP6ClientIAID="" 871T1="" # Voir 2462 872T2="" # Voir 2462 873DHCP6ServerDUID="" 874DHCP6CurrentTransactionID="" # devrait etre utilise pour matcher une 875# reponse et mis a jour en mode client par une valeur aleatoire pour 876# laquelle on attend un retour de la part d'un serveur. 877DHCP6PrefVal="" # la valeur de preference a utiliser dans 878# les options preference 879 880# Emitted by : 881# - server : ADVERTISE, REPLY, RECONFIGURE, RELAY-REPL (vers relay) 882# - client : SOLICIT, REQUEST, CONFIRM, RENEW, REBIND, RELEASE, DECLINE, 883# INFORMATION REQUEST 884# - relay : RELAY-FORW (toward server) 885 886##################################################################### 887## DHCPv6 messages sent between Clients and Servers (types 1 to 11) 888# Comme specifie en section 15.1 de la RFC 3315, les valeurs de 889# transaction id sont selectionnees de maniere aleatoire par le client 890# a chaque emission et doivent matcher dans les reponses faites par 891# les clients 892class DHCP6(_DHCP6OptGuessPayload): 893 name = "DHCPv6 Generic Message" 894 fields_desc = [ ByteEnumField("msgtype",None,dhcp6types), 895 X3BytesField("trid",0x000000) ] 896 overload_fields = { UDP: {"sport": 546, "dport": 547} } 897 898 def hashret(self): 899 return struct.pack("!I", self.trid)[1:4] 900 901#### DHCPv6 Relay Message Option #################################### 902 903# Relayed message is seen as a payload. 904class DHCP6OptRelayMsg(_DHCP6OptGuessPayload): # RFC sect 22.10 905 name = "DHCP6 Relay Message Option" 906 fields_desc = [ ShortEnumField("optcode", 9, dhcp6opts), 907 FieldLenField("optlen", None, fmt="!H", 908 length_of="message"), 909 PacketLenField("message", DHCP6(), DHCP6, 910 length_from=lambda p: p.optlen) ] 911 912##################################################################### 913# Solicit Message : sect 17.1.1 RFC3315 914# - sent by client 915# - must include a client identifier option 916# - the client may include IA options for any IAs to which it wants the 917# server to assign address 918# - The client use IA_NA options to request the assignment of 919# non-temporary addresses and uses IA_TA options to request the 920# assignment of temporary addresses 921# - The client should include an Option Request option to indicate the 922# options the client is interested in receiving (eventually 923# including hints) 924# - The client includes a Reconfigure Accept option if is willing to 925# accept Reconfigure messages from the server. 926# Le cas du send and reply est assez particulier car suivant la 927# presence d'une option rapid commit dans le solicit, l'attente 928# s'arrete au premier message de reponse recu ou alors apres un 929# timeout. De la meme maniere, si un message Advertise arrive avec une 930# valeur de preference de 255, il arrete l'attente et envoie une 931# Request. 932# - The client announces its intention to use DHCP authentication by 933# including an Authentication option in its solicit message. The 934# server selects a key for the client based on the client's DUID. The 935# client and server use that key to authenticate all DHCP messages 936# exchanged during the session 937 938class DHCP6_Solicit(DHCP6): 939 name = "DHCPv6 Solicit Message" 940 msgtype = 1 941 overload_fields = { UDP: {"sport": 546, "dport": 547} } 942 943##################################################################### 944# Advertise Message 945# - sent by server 946# - Includes a server identifier option 947# - Includes a client identifier option 948# - the client identifier option must match the client's DUID 949# - transaction ID must match 950 951class DHCP6_Advertise(DHCP6): 952 name = "DHCPv6 Advertise Message" 953 msgtype = 2 954 overload_fields = { UDP: {"sport": 547, "dport": 546} } 955 956 def answers(self, other): 957 return (isinstance(other,DHCP6_Solicit) and 958 other.msgtype == 1 and 959 self.trid == other.trid) 960 961##################################################################### 962# Request Message 963# - sent by clients 964# - includes a server identifier option 965# - the content of Server Identifier option must match server's DUID 966# - includes a client identifier option 967# - must include an ORO Option (even with hints) p40 968# - can includes a reconfigure Accept option indicating whether or 969# not the client is willing to accept Reconfigure messages from 970# the server (p40) 971# - When the server receives a Request message via unicast from a 972# client to which the server has not sent a unicast option, the server 973# discards the Request message and responds with a Reply message 974# containing Status Code option with the value UseMulticast, a Server 975# Identifier Option containing the server's DUID, the client 976# Identifier option from the client message and no other option. 977 978class DHCP6_Request(DHCP6): 979 name = "DHCPv6 Request Message" 980 msgtype = 3 981 982##################################################################### 983# Confirm Message 984# - sent by clients 985# - must include a client identifier option 986# - When the server receives a Confirm Message, the server determines 987# whether the addresses in the Confirm message are appropriate for the 988# link to which the client is attached. cf p50 989 990class DHCP6_Confirm(DHCP6): 991 name = "DHCPv6 Confirm Message" 992 msgtype = 4 993 994##################################################################### 995# Renew Message 996# - sent by clients 997# - must include a server identifier option 998# - content of server identifier option must match the server's identifier 999# - must include a client identifier option 1000# - the clients includes any IA assigned to the interface that may 1001# have moved to a new link, along with the addresses associated with 1002# those IAs in its confirm messages 1003# - When the server receives a Renew message that contains an IA 1004# option from a client, it locates the client's binding and verifies 1005# that the information in the IA from the client matches the 1006# information for that client. If the server cannot find a client 1007# entry for the IA the server returns the IA containing no addresses 1008# with a status code option est to NoBinding in the Reply message. cf 1009# p51 pour le reste. 1010 1011class DHCP6_Renew(DHCP6): 1012 name = "DHCPv6 Renew Message" 1013 msgtype = 5 1014 1015##################################################################### 1016# Rebind Message 1017# - sent by clients 1018# - must include a client identifier option 1019# cf p52 1020 1021class DHCP6_Rebind(DHCP6): 1022 name = "DHCPv6 Rebind Message" 1023 msgtype = 6 1024 1025##################################################################### 1026# Reply Message 1027# - sent by servers 1028# - the message must include a server identifier option 1029# - transaction-id field must match the value of original message 1030# The server includes a Rapid Commit option in the Reply message to 1031# indicate that the reply is in response to a solicit message 1032# - if the client receives a reply message with a Status code option 1033# with the value UseMulticast, the client records the receipt of the 1034# message and sends subsequent messages to the server through the 1035# interface on which the message was received using multicast. The 1036# client resends the original message using multicast 1037# - When the client receives a NotOnLink status from the server in 1038# response to a Confirm message, the client performs DHCP server 1039# solicitation as described in section 17 and client-initiated 1040# configuration as descrribed in section 18 (RFC 3315) 1041# - when the client receives a NotOnLink status from the server in 1042# response to a Request, the client can either re-issue the Request 1043# without specifying any addresses or restart the DHCP server 1044# discovery process. 1045# - the server must include a server identifier option containing the 1046# server's DUID in the Reply message 1047 1048class DHCP6_Reply(DHCP6): 1049 name = "DHCPv6 Reply Message" 1050 msgtype = 7 1051 1052 overload_fields = { UDP: {"sport": 547, "dport": 546} } 1053 1054 def answers(self, other): 1055 1056 types = (DHCP6_InfoRequest, DHCP6_Confirm, DHCP6_Rebind, DHCP6_Decline, DHCP6_Request, DHCP6_Release, DHCP6_Renew) 1057 1058 return (isinstance(other, types) and 1059 self.trid == other.trid) 1060 1061##################################################################### 1062# Release Message 1063# - sent by clients 1064# - must include a server identifier option 1065# cf p53 1066 1067class DHCP6_Release(DHCP6): 1068 name = "DHCPv6 Release Message" 1069 msgtype = 8 1070 1071##################################################################### 1072# Decline Message 1073# - sent by clients 1074# - must include a client identifier option 1075# - Server identifier option must match server identifier 1076# - The addresses to be declined must be included in the IAs. Any 1077# addresses for the IAs the client wishes to continue to use should 1078# not be in added to the IAs. 1079# - cf p54 1080 1081class DHCP6_Decline(DHCP6): 1082 name = "DHCPv6 Decline Message" 1083 msgtype = 9 1084 1085##################################################################### 1086# Reconfigure Message 1087# - sent by servers 1088# - must be unicast to the client 1089# - must include a server identifier option 1090# - must include a client identifier option that contains the client DUID 1091# - must contain a Reconfigure Message Option and the message type 1092# must be a valid value 1093# - the server sets the transaction-id to 0 1094# - The server must use DHCP Authentication in the Reconfigure 1095# message. Autant dire que ca va pas etre le type de message qu'on va 1096# voir le plus souvent. 1097 1098class DHCP6_Reconf(DHCP6): 1099 name = "DHCPv6 Reconfigure Message" 1100 msgtype = 10 1101 overload_fields = { UDP: { "sport": 547, "dport": 546 } } 1102 1103 1104##################################################################### 1105# Information-Request Message 1106# - sent by clients when needs configuration information but no 1107# addresses. 1108# - client should include a client identifier option to identify 1109# itself. If it doesn't the server is not able to return client 1110# specific options or the server can choose to not respond to the 1111# message at all. The client must include a client identifier option 1112# if the message will be authenticated. 1113# - client must include an ORO of option she's interested in receiving 1114# (can include hints) 1115 1116class DHCP6_InfoRequest(DHCP6): 1117 name = "DHCPv6 Information Request Message" 1118 msgtype = 11 1119 1120##################################################################### 1121# sent between Relay Agents and Servers 1122# 1123# Normalement, doit inclure une option "Relay Message Option" 1124# peut en inclure d'autres. 1125# voir section 7.1 de la 3315 1126 1127# Relay-Forward Message 1128# - sent by relay agents to servers 1129# If the relay agent relays messages to the All_DHCP_Servers multicast 1130# address or other multicast addresses, it sets the Hop Limit field to 1131# 32. 1132 1133class DHCP6_RelayForward(_DHCP6OptGuessPayload,Packet): 1134 name = "DHCPv6 Relay Forward Message (Relay Agent/Server Message)" 1135 fields_desc = [ ByteEnumField("msgtype", 12, dhcp6types), 1136 ByteField("hopcount", None), 1137 IP6Field("linkaddr", "::"), 1138 IP6Field("peeraddr", "::") ] 1139 overload_fields = { UDP: { "sport": 547, "dport": 547 } } 1140 def hashret(self): # we filter on peer address field 1141 return inet_pton(socket.AF_INET6, self.peeraddr) 1142 1143##################################################################### 1144# sent between Relay Agents and Servers 1145# Normalement, doit inclure une option "Relay Message Option" 1146# peut en inclure d'autres. 1147# Les valeurs des champs hop-count, link-addr et peer-addr 1148# sont copiees du message Forward associe. POur le suivi de session. 1149# Pour le moment, comme decrit dans le commentaire, le hashret 1150# se limite au contenu du champ peer address. 1151# Voir section 7.2 de la 3315. 1152 1153# Relay-Reply Message 1154# - sent by servers to relay agents 1155# - if the solicit message was received in a Relay-Forward message, 1156# the server constructs a relay-reply message with the Advertise 1157# message in the payload of a relay-message. cf page 37/101. Envoie de 1158# ce message en unicast au relay-agent. utilisation de l'adresse ip 1159# presente en ip source du paquet recu 1160 1161class DHCP6_RelayReply(DHCP6_RelayForward): 1162 name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)" 1163 msgtype = 13 1164 def hashret(self): # We filter on peer address field. 1165 return inet_pton(socket.AF_INET6, self.peeraddr) 1166 def answers(self, other): 1167 return (isinstance(other, DHCP6_RelayForward) and 1168 self.hopcount == other.hopcount and 1169 self.linkaddr == other.linkaddr and 1170 self.peeraddr == other.peeraddr ) 1171 1172 1173dhcp6_cls_by_type = { 1: "DHCP6_Solicit", 1174 2: "DHCP6_Advertise", 1175 3: "DHCP6_Request", 1176 4: "DHCP6_Confirm", 1177 5: "DHCP6_Renew", 1178 6: "DHCP6_Rebind", 1179 7: "DHCP6_Reply", 1180 8: "DHCP6_Release", 1181 9: "DHCP6_Decline", 1182 10: "DHCP6_Reconf", 1183 11: "DHCP6_InfoRequest", 1184 12: "DHCP6_RelayForward", 1185 13: "DHCP6_RelayReply" } 1186 1187def _dhcp6_dispatcher(x, *args, **kargs): 1188 cls = conf.raw_layer 1189 if len(x) >= 2: 1190 cls = get_cls(dhcp6_cls_by_type.get(orb(x[0]), "Raw"), conf.raw_layer) 1191 return cls(x, *args, **kargs) 1192 1193bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 547 } ) 1194bind_bottom_up(UDP, _dhcp6_dispatcher, { "dport": 546 } ) 1195 1196 1197 1198class DHCPv6_am(AnsweringMachine): 1199 function_name = "dhcp6d" 1200 filter = "udp and port 546 and port 547" 1201 send_function = staticmethod(send) 1202 def usage(self): 1203 msg = """ 1204DHCPv6_am.parse_options( dns="2001:500::1035", domain="localdomain, local", 1205 duid=None, iface=conf.iface6, advpref=255, sntpservers=None, 1206 sipdomains=None, sipservers=None, 1207 nisdomain=None, nisservers=None, 1208 nispdomain=None, nispservers=None, 1209 bcmcsdomains=None, bcmcsservers=None) 1210 1211 debug : When set, additional debugging information is printed. 1212 1213 duid : some DUID class (DUID_LLT, DUID_LL or DUID_EN). If none 1214 is provided a DUID_LLT is constructed based on the MAC 1215 address of the sending interface and launch time of dhcp6d 1216 answering machine. 1217 1218 iface : the interface to listen/reply on if you do not want to use 1219 conf.iface6. 1220 1221 advpref : Value in [0,255] given to Advertise preference field. 1222 By default, 255 is used. Be aware that this specific 1223 value makes clients stops waiting for further Advertise 1224 messages from other servers. 1225 1226 dns : list of recursive DNS servers addresses (as a string or list). 1227 By default, it is set empty and the associated DHCP6OptDNSServers 1228 option is inactive. See RFC 3646 for details. 1229 domain : a list of DNS search domain (as a string or list). By default, 1230 it is empty and the associated DHCP6OptDomains option is inactive. 1231 See RFC 3646 for details. 1232 1233 sntpservers : a list of SNTP servers IPv6 addresses. By default, 1234 it is empty and the associated DHCP6OptSNTPServers option 1235 is inactive. 1236 1237 sipdomains : a list of SIP domains. By default, it is empty and the 1238 associated DHCP6OptSIPDomains option is inactive. See RFC 3319 1239 for details. 1240 sipservers : a list of SIP servers IPv6 addresses. By default, it is 1241 empty and the associated DHCP6OptSIPDomains option is inactive. 1242 See RFC 3319 for details. 1243 1244 nisdomain : a list of NIS domains. By default, it is empty and the 1245 associated DHCP6OptNISDomains option is inactive. See RFC 3898 1246 for details. See RFC 3646 for details. 1247 nisservers : a list of NIS servers IPv6 addresses. By default, it is 1248 empty and the associated DHCP6OptNISServers option is inactive. 1249 See RFC 3646 for details. 1250 1251 nispdomain : a list of NIS+ domains. By default, it is empty and the 1252 associated DHCP6OptNISPDomains option is inactive. See RFC 3898 1253 for details. 1254 nispservers : a list of NIS+ servers IPv6 addresses. By default, it is 1255 empty and the associated DHCP6OptNISServers option is inactive. 1256 See RFC 3898 for details. 1257 1258 bcmcsdomain : a list of BCMCS domains. By default, it is empty and the 1259 associated DHCP6OptBCMCSDomains option is inactive. See RFC 4280 1260 for details. 1261 bcmcsservers : a list of BCMCS servers IPv6 addresses. By default, it is 1262 empty and the associated DHCP6OptBCMCSServers option is inactive. 1263 See RFC 4280 for details. 1264 1265 If you have a need for others, just ask ... or provide a patch.""" 1266 print(msg) 1267 1268 def parse_options(self, dns="2001:500::1035", domain="localdomain, local", 1269 startip="2001:db8::1", endip="2001:db8::20", duid=None, 1270 sntpservers=None, sipdomains=None, sipservers=None, 1271 nisdomain=None, nisservers=None, nispdomain=None, 1272 nispservers=None, bcmcsservers=None, bcmcsdomains=None, 1273 iface=None, debug=0, advpref=255): 1274 def norm_list(val, param_name): 1275 if val is None: 1276 return None 1277 if isinstance(val, list): 1278 return val 1279 elif isinstance(val, str): 1280 l = val.split(',') 1281 return [x.strip() for x in l] 1282 else: 1283 print("Bad '%s' parameter provided." % param_name) 1284 self.usage() 1285 return -1 1286 1287 if iface is None: 1288 iface = conf.iface6 1289 1290 self.debug = debug 1291 1292 # Dictionary of provided DHCPv6 options, keyed by option type 1293 self.dhcpv6_options={} 1294 1295 for o in [(dns, "dns", 23, lambda x: DHCP6OptDNSServers(dnsservers=x)), 1296 (domain, "domain", 24, lambda x: DHCP6OptDNSDomains(dnsdomains=x)), 1297 (sntpservers, "sntpservers", 31, lambda x: DHCP6OptSNTPServers(sntpservers=x)), 1298 (sipservers, "sipservers", 22, lambda x: DHCP6OptSIPServers(sipservers=x)), 1299 (sipdomains, "sipdomains", 21, lambda x: DHCP6OptSIPDomains(sipdomains=x)), 1300 (nisservers, "nisservers", 27, lambda x: DHCP6OptNISServers(nisservers=x)), 1301 (nisdomain, "nisdomain", 29, lambda x: DHCP6OptNISDomain(nisdomain=(x+[""])[0])), 1302 (nispservers, "nispservers", 28, lambda x: DHCP6OptNISPServers(nispservers=x)), 1303 (nispdomain, "nispdomain", 30, lambda x: DHCP6OptNISPDomain(nispdomain=(x+[""])[0])), 1304 (bcmcsservers, "bcmcsservers", 33, lambda x: DHCP6OptBCMCSServers(bcmcsservers=x)), 1305 (bcmcsdomains, "bcmcsdomains", 34, lambda x: DHCP6OptBCMCSDomains(bcmcsdomains=x))]: 1306 1307 opt = norm_list(o[0], o[1]) 1308 if opt == -1: # Usage() was triggered 1309 return False 1310 elif opt is None: # We won't return that option 1311 pass 1312 else: 1313 self.dhcpv6_options[o[2]] = o[3](opt) 1314 1315 if self.debug: 1316 print("\n[+] List of active DHCPv6 options:") 1317 opts = sorted(self.dhcpv6_options) 1318 for i in opts: 1319 print(" %d: %s" % (i, repr(self.dhcpv6_options[i]))) 1320 1321 # Preference value used in Advertise. 1322 self.advpref = advpref 1323 1324 # IP Pool 1325 self.startip = startip 1326 self.endip = endip 1327 # XXX TODO Check IPs are in same subnet 1328 1329 #### 1330 # The interface we are listening/replying on 1331 self.iface = iface 1332 1333 #### 1334 # Generate a server DUID 1335 if duid is not None: 1336 self.duid = duid 1337 else: 1338 # Timeval 1339 epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0) 1340 delta = time.mktime(epoch) - EPOCH 1341 timeval = time.time() - delta 1342 1343 # Mac Address 1344 rawmac = get_if_raw_hwaddr(iface)[1] 1345 mac = ":".join("%.02x" % orb(x) for x in rawmac) 1346 1347 self.duid = DUID_LLT(timeval = timeval, lladdr = mac) 1348 1349 if self.debug: 1350 print("\n[+] Our server DUID:") 1351 self.duid.show(label_lvl=" "*4) 1352 1353 #### 1354 # Find the source address we will use 1355 try: 1356 addr = next(x for x in in6_getifaddr() if x[2] == iface and in6_islladdr(x[0])) 1357 except StopIteration: 1358 warning("Unable to get a Link-Local address") 1359 return 1360 else: 1361 self.src_addr = addr[0] 1362 1363 #### 1364 # Our leases 1365 self.leases = {} 1366 1367 1368 if self.debug: 1369 print("\n[+] Starting DHCPv6 service on %s:" % self.iface) 1370 1371 def is_request(self, p): 1372 if not IPv6 in p: 1373 return False 1374 1375 src = p[IPv6].src 1376 1377 p = p[IPv6].payload 1378 if not isinstance(p, UDP) or p.sport != 546 or p.dport != 547 : 1379 return False 1380 1381 p = p.payload 1382 if not isinstance(p, DHCP6): 1383 return False 1384 1385 # Message we considered client messages : 1386 # Solicit (1), Request (3), Confirm (4), Renew (5), Rebind (6) 1387 # Decline (9), Release (8), Information-request (11), 1388 if not (p.msgtype in [1, 3, 4, 5, 6, 8, 9, 11]): 1389 return False 1390 1391 # Message validation following section 15 of RFC 3315 1392 1393 if ((p.msgtype == 1) or # Solicit 1394 (p.msgtype == 6) or # Rebind 1395 (p.msgtype == 4)): # Confirm 1396 if ((not DHCP6OptClientId in p) or 1397 DHCP6OptServerId in p): 1398 return False 1399 1400 if (p.msgtype == 6 or # Rebind 1401 p.msgtype == 4): # Confirm 1402 # XXX We do not reply to Confirm or Rebind as we 1403 # XXX do not support address assignment 1404 return False 1405 1406 elif (p.msgtype == 3 or # Request 1407 p.msgtype == 5 or # Renew 1408 p.msgtype == 8): # Release 1409 1410 # Both options must be present 1411 if ((not DHCP6OptServerId in p) or 1412 (not DHCP6OptClientId in p)): 1413 return False 1414 # provided server DUID must match ours 1415 duid = p[DHCP6OptServerId].duid 1416 if not isinstance(duid, type(self.duid)): 1417 return False 1418 if raw(duid) != raw(self.duid): 1419 return False 1420 1421 if (p.msgtype == 5 or # Renew 1422 p.msgtype == 8): # Release 1423 # XXX We do not reply to Renew or Release as we 1424 # XXX do not support address assignment 1425 return False 1426 1427 elif p.msgtype == 9: # Decline 1428 # XXX We should check if we are tracking that client 1429 if not self.debug: 1430 return False 1431 1432 bo = Color.bold 1433 g = Color.green + bo 1434 b = Color.blue + bo 1435 n = Color.normal 1436 r = Color.red 1437 1438 vendor = in6_addrtovendor(src) 1439 if (vendor and vendor != "UNKNOWN"): 1440 vendor = " [" + b + vendor + n + "]" 1441 else: 1442 vendor = "" 1443 src = bo + src + n 1444 1445 it = p 1446 addrs = [] 1447 while it: 1448 l = [] 1449 if isinstance(it, DHCP6OptIA_NA): 1450 l = it.ianaopts 1451 elif isinstance(it, DHCP6OptIA_TA): 1452 l = it.iataopts 1453 1454 addrs += [x.addr for x in l if isinstance(x, DHCP6OptIAAddress)] 1455 it = it.payload 1456 1457 addrs = [bo + x + n for x in addrs] 1458 if self.debug: 1459 msg = r + "[DEBUG]" + n + " Received " + g + "Decline" + n 1460 msg += " from " + bo + src + vendor + " for " 1461 msg += ", ".join(addrs)+ n 1462 print(msg) 1463 1464 # See sect 18.1.7 1465 1466 # Sent by a client to warn us she has determined 1467 # one or more addresses assigned to her is already 1468 # used on the link. 1469 # We should simply log that fact. No messaged should 1470 # be sent in return. 1471 1472 # - Message must include a Server identifier option 1473 # - the content of the Server identifier option must 1474 # match the server's identifier 1475 # - the message must include a Client Identifier option 1476 return False 1477 1478 elif p.msgtype == 11: # Information-Request 1479 if DHCP6OptServerId in p: 1480 duid = p[DHCP6OptServerId].duid 1481 if not isinstance(duid, type(self.duid)): 1482 return False 1483 if raw(duid) != raw(self.duid): 1484 return False 1485 if ((DHCP6OptIA_NA in p) or 1486 (DHCP6OptIA_TA in p) or 1487 (DHCP6OptIA_PD in p)): 1488 return False 1489 else: 1490 return False 1491 1492 return True 1493 1494 def print_reply(self, req, reply): 1495 def norm(s): 1496 if s.startswith("DHCPv6 "): 1497 s = s[7:] 1498 if s.endswith(" Message"): 1499 s = s[:-8] 1500 return s 1501 1502 if reply is None: 1503 return 1504 1505 bo = Color.bold 1506 g = Color.green + bo 1507 b = Color.blue + bo 1508 n = Color.normal 1509 reqtype = g + norm(req.getlayer(UDP).payload.name) + n 1510 reqsrc = req.getlayer(IPv6).src 1511 vendor = in6_addrtovendor(reqsrc) 1512 if (vendor and vendor != "UNKNOWN"): 1513 vendor = " [" + b + vendor + n + "]" 1514 else: 1515 vendor = "" 1516 reqsrc = bo + reqsrc + n 1517 reptype = g + norm(reply.getlayer(UDP).payload.name) + n 1518 1519 print("Sent %s answering to %s from %s%s" % (reptype, reqtype, reqsrc, vendor)) 1520 1521 def make_reply(self, req): 1522 p = req[IPv6] 1523 req_src = p.src 1524 1525 p = p.payload.payload 1526 1527 msgtype = p.msgtype 1528 trid = p.trid 1529 1530 if msgtype == 1: # SOLICIT (See Sect 17.1 and 17.2 of RFC 3315) 1531 1532 # XXX We don't support address or prefix assignment 1533 # XXX We also do not support relay function --arno 1534 1535 client_duid = p[DHCP6OptClientId].duid 1536 resp = IPv6(src=self.src_addr, dst=req_src) 1537 resp /= UDP(sport=547, dport=546) 1538 1539 if p.haslayer(DHCP6OptRapidCommit): 1540 # construct a Reply packet 1541 resp /= DHCP6_Reply(trid=trid) 1542 resp /= DHCP6OptRapidCommit() # See 17.1.2 1543 resp /= DHCP6OptServerId(duid = self.duid) 1544 resp /= DHCP6OptClientId(duid = client_duid) 1545 1546 else: # No Rapid Commit in the packet. Reply with an Advertise 1547 1548 if (p.haslayer(DHCP6OptIA_NA) or 1549 p.haslayer(DHCP6OptIA_TA)): 1550 # XXX We don't assign addresses at the moment 1551 msg = "Scapy6 dhcp6d does not support address assignment" 1552 resp /= DHCP6_Advertise(trid = trid) 1553 resp /= DHCP6OptStatusCode(statuscode=2, statusmsg=msg) 1554 resp /= DHCP6OptServerId(duid = self.duid) 1555 resp /= DHCP6OptClientId(duid = client_duid) 1556 1557 elif p.haslayer(DHCP6OptIA_PD): 1558 # XXX We don't assign prefixes at the moment 1559 msg = "Scapy6 dhcp6d does not support prefix assignment" 1560 resp /= DHCP6_Advertise(trid = trid) 1561 resp /= DHCP6OptStatusCode(statuscode=6, statusmsg=msg) 1562 resp /= DHCP6OptServerId(duid = self.duid) 1563 resp /= DHCP6OptClientId(duid = client_duid) 1564 1565 else: # Usual case, no request for prefixes or addresse 1566 resp /= DHCP6_Advertise(trid = trid) 1567 resp /= DHCP6OptPref(prefval = self.advpref) 1568 resp /= DHCP6OptServerId(duid = self.duid) 1569 resp /= DHCP6OptClientId(duid = client_duid) 1570 resp /= DHCP6OptReconfAccept() 1571 1572 # See which options should be included 1573 reqopts = [] 1574 if p.haslayer(DHCP6OptOptReq): # add only asked ones 1575 reqopts = p[DHCP6OptOptReq].reqopts 1576 for o, opt in six.iteritems(self.dhcpv6_options): 1577 if o in reqopts: 1578 resp /= opt 1579 else: # advertise everything we have available 1580 for o, opt in six.iteritems(self.dhcpv6_options): 1581 resp /= opt 1582 1583 return resp 1584 1585 elif msgtype == 3: #REQUEST (INFO-REQUEST is further below) 1586 client_duid = p[DHCP6OptClientId].duid 1587 resp = IPv6(src=self.src_addr, dst=req_src) 1588 resp /= UDP(sport=547, dport=546) 1589 resp /= DHCP6_Solicit(trid=trid) 1590 resp /= DHCP6OptServerId(duid = self.duid) 1591 resp /= DHCP6OptClientId(duid = client_duid) 1592 1593 # See which options should be included 1594 reqopts = [] 1595 if p.haslayer(DHCP6OptOptReq): # add only asked ones 1596 reqopts = p[DHCP6OptOptReq].reqopts 1597 for o, opt in six.iteritems(self.dhcpv6_options): 1598 if o in reqopts: 1599 resp /= opt 1600 else: 1601 # advertise everything we have available. 1602 # Should not happen has clients MUST include 1603 # and ORO in requests (sec 18.1.1) -- arno 1604 for o, opt in six.iteritems(self.dhcpv6_options): 1605 resp /= opt 1606 1607 return resp 1608 1609 elif msgtype == 4: # CONFIRM 1610 # see Sect 18.1.2 1611 1612 # Client want to check if addresses it was assigned 1613 # are still appropriate 1614 1615 # Server must discard any Confirm messages that 1616 # do not include a Client Identifier option OR 1617 # THAT DO INCLUDE a Server Identifier Option 1618 1619 # XXX we must discard the SOLICIT if it is received with 1620 # a unicast destination address 1621 1622 pass 1623 1624 elif msgtype == 5: # RENEW 1625 # see Sect 18.1.3 1626 1627 # Clients want to extend lifetime of assigned addresses 1628 # and update configuration parameters. This message is sent 1629 # specifically to the server that provided her the info 1630 1631 # - Received message must include a Server Identifier 1632 # option. 1633 # - the content of server identifier option must match 1634 # the server's identifier. 1635 # - the message must include a Client identifier option 1636 1637 pass 1638 1639 elif msgtype == 6: # REBIND 1640 # see Sect 18.1.4 1641 1642 # Same purpose as the Renew message but sent to any 1643 # available server after he received no response 1644 # to its previous Renew message. 1645 1646 1647 # - Message must include a Client Identifier Option 1648 # - Message can't include a Server identifier option 1649 1650 # XXX we must discard the SOLICIT if it is received with 1651 # a unicast destination address 1652 1653 pass 1654 1655 elif msgtype == 8: # RELEASE 1656 # See section 18.1.6 1657 1658 # Message is sent to the server to indicate that 1659 # she will no longer use the addresses that was assigned 1660 # We should parse the message and verify our dictionary 1661 # to log that fact. 1662 1663 1664 # - The message must include a server identifier option 1665 # - The content of the Server Identifier option must 1666 # match the server's identifier 1667 # - the message must include a Client Identifier option 1668 1669 pass 1670 1671 elif msgtype == 9: # DECLINE 1672 # See section 18.1.7 1673 pass 1674 1675 elif msgtype == 11: # INFO-REQUEST 1676 client_duid = None 1677 if not p.haslayer(DHCP6OptClientId): 1678 if self.debug: 1679 warning("Received Info Request message without Client Id option") 1680 else: 1681 client_duid = p[DHCP6OptClientId].duid 1682 1683 resp = IPv6(src=self.src_addr, dst=req_src) 1684 resp /= UDP(sport=547, dport=546) 1685 resp /= DHCP6_Reply(trid=trid) 1686 resp /= DHCP6OptServerId(duid = self.duid) 1687 1688 if client_duid: 1689 resp /= DHCP6OptClientId(duid = client_duid) 1690 1691 # Stack requested options if available 1692 reqopts = [] 1693 if p.haslayer(DHCP6OptOptReq): 1694 reqopts = p[DHCP6OptOptReq].reqopts 1695 for o, opt in six.iteritems(self.dhcpv6_options): 1696 resp /= opt 1697 1698 return resp 1699 1700 else: 1701 # what else ? 1702 pass 1703 1704 # - We won't support reemission 1705 # - We won't support relay role, nor relay forwarded messages 1706 # at the beginning 1707