Coverage for /root/GitHubProjects/impacket/impacket/ICMP6.py : 55%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
10import array
11import struct
13from impacket.ImpactPacket import Header, Data, array_tobytes
14from impacket.IP6_Address import IP6_Address
17class ICMP6(Header):
18 #IP Protocol number for ICMP6
19 IP_PROTOCOL_NUMBER = 58
20 protocol = IP_PROTOCOL_NUMBER #ImpactDecoder uses the constant "protocol" as the IP Protocol Number
22 #Size of ICMP6 header (excluding payload)
23 HEADER_SIZE = 4
25 #ICMP6 Message Type numbers
26 DESTINATION_UNREACHABLE = 1
27 PACKET_TOO_BIG = 2
28 TIME_EXCEEDED = 3
29 PARAMETER_PROBLEM = 4
30 ECHO_REQUEST = 128
31 ECHO_REPLY = 129
32 ROUTER_SOLICITATION = 133
33 ROUTER_ADVERTISEMENT = 134
34 NEIGHBOR_SOLICITATION = 135
35 NEIGHBOR_ADVERTISEMENT = 136
36 REDIRECT_MESSAGE = 137
37 NODE_INFORMATION_QUERY = 139
38 NODE_INFORMATION_REPLY = 140
40 #Destination Unreachable codes
41 NO_ROUTE_TO_DESTINATION = 0
42 ADMINISTRATIVELY_PROHIBITED = 1
43 BEYOND_SCOPE_OF_SOURCE_ADDRESS = 2
44 ADDRESS_UNREACHABLE = 3
45 PORT_UNREACHABLE = 4
46 SOURCE_ADDRESS_FAILED_INGRESS_EGRESS_POLICY = 5
47 REJECT_ROUTE_TO_DESTINATION = 6
49 #Time Exceeded codes
50 HOP_LIMIT_EXCEEDED_IN_TRANSIT = 0
51 FRAGMENT_REASSEMBLY_TIME_EXCEEDED = 1
53 #Parameter problem codes
54 ERRONEOUS_HEADER_FIELD_ENCOUNTERED = 0
55 UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED = 1
56 UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED = 2
58 #Node Information codes
59 NODE_INFORMATION_QUERY_IPV6 = 0
60 NODE_INFORMATION_QUERY_NAME_OR_EMPTY = 1
61 NODE_INFORMATION_QUERY_IPV4 = 2
62 NODE_INFORMATION_REPLY_SUCCESS = 0
63 NODE_INFORMATION_REPLY_REFUSED = 1
64 NODE_INFORMATION_REPLY_UNKNOWN_QTYPE = 2
66 #Node Information qtypes
67 NODE_INFORMATION_QTYPE_NOOP = 0
68 NODE_INFORMATION_QTYPE_UNUSED = 1
69 NODE_INFORMATION_QTYPE_NODENAME = 2
70 NODE_INFORMATION_QTYPE_NODEADDRS = 3
71 NODE_INFORMATION_QTYPE_IPv4ADDRS = 4
73 #ICMP Message semantic types (error or informational)
74 ERROR_MESSAGE = 0
75 INFORMATIONAL_MESSAGE = 1
77 #ICMP message dictionary - specifying text descriptions and valid message codes
78 #Key: ICMP message number
79 #Data: Tuple ( Message Type (error/informational), Text description, Codes dictionary (can be None) )
80 #Codes dictionary
81 #Key: Code number
82 #Data: Text description
84 #ICMP message dictionary tuple indexes
85 MSG_TYPE_INDEX = 0
86 DESCRIPTION_INDEX = 1
87 CODES_INDEX = 2
89 icmp_messages = {
90 DESTINATION_UNREACHABLE : (ERROR_MESSAGE, "Destination unreachable",
91 { NO_ROUTE_TO_DESTINATION : "No route to destination",
92 ADMINISTRATIVELY_PROHIBITED : "Administratively prohibited",
93 BEYOND_SCOPE_OF_SOURCE_ADDRESS : "Beyond scope of source address",
94 ADDRESS_UNREACHABLE : "Address unreachable",
95 PORT_UNREACHABLE : "Port unreachable",
96 SOURCE_ADDRESS_FAILED_INGRESS_EGRESS_POLICY : "Source address failed ingress/egress policy",
97 REJECT_ROUTE_TO_DESTINATION : "Reject route to destination"
98 }),
99 PACKET_TOO_BIG : (ERROR_MESSAGE, "Packet too big", None),
100 TIME_EXCEEDED : (ERROR_MESSAGE, "Time exceeded",
101 {HOP_LIMIT_EXCEEDED_IN_TRANSIT : "Hop limit exceeded in transit",
102 FRAGMENT_REASSEMBLY_TIME_EXCEEDED : "Fragment reassembly time exceeded"
103 }),
104 PARAMETER_PROBLEM : (ERROR_MESSAGE, "Parameter problem",
105 {
106 ERRONEOUS_HEADER_FIELD_ENCOUNTERED : "Erroneous header field encountered",
107 UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED : "Unrecognized Next Header type encountered",
108 UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED : "Unrecognized IPv6 Option Encountered"
109 }),
110 ECHO_REQUEST : (INFORMATIONAL_MESSAGE, "Echo request", None),
111 ECHO_REPLY : (INFORMATIONAL_MESSAGE, "Echo reply", None),
112 ROUTER_SOLICITATION : (INFORMATIONAL_MESSAGE, "Router Solicitation", None),
113 ROUTER_ADVERTISEMENT : (INFORMATIONAL_MESSAGE, "Router Advertisement", None),
114 NEIGHBOR_SOLICITATION : (INFORMATIONAL_MESSAGE, "Neighbor Solicitation", None),
115 NEIGHBOR_ADVERTISEMENT : (INFORMATIONAL_MESSAGE, "Neighbor Advertisement", None),
116 REDIRECT_MESSAGE : (INFORMATIONAL_MESSAGE, "Redirect Message", None),
117 NODE_INFORMATION_QUERY: (INFORMATIONAL_MESSAGE, "Node Information Query", None),
118 NODE_INFORMATION_REPLY: (INFORMATIONAL_MESSAGE, "Node Information Reply", None),
119 }
124############################################################################
125 def __init__(self, buffer = None):
126 Header.__init__(self, self.HEADER_SIZE)
127 if (buffer):
128 self.load_header(buffer)
130 def get_header_size(self):
131 return self.HEADER_SIZE
133 def get_ip_protocol_number(self):
134 return self.IP_PROTOCOL_NUMBER
136 def __str__(self):
137 type = self.get_type()
138 code = self.get_code()
139 checksum = self.get_checksum()
141 s = "ICMP6 - Type: " + str(type) + " - " + self.__get_message_description() + "\n"
142 s += "Code: " + str(code)
143 if (self.__get_code_description() != ""):
144 s += " - " + self.__get_code_description()
145 s += "\n"
146 s += "Checksum: " + str(checksum) + "\n"
147 return s
149 def __get_message_description(self):
150 return self.icmp_messages[self.get_type()][self.DESCRIPTION_INDEX]
152 def __get_code_description(self):
153 code_dictionary = self.icmp_messages[self.get_type()][self.CODES_INDEX]
154 if (code_dictionary is None):
155 return ""
156 else:
157 return code_dictionary[self.get_code()]
159############################################################################
160 def get_type(self):
161 return (self.get_byte(0))
163 def get_code(self):
164 return (self.get_byte(1))
166 def get_checksum(self):
167 return (self.get_word(2))
169############################################################################
170 def set_type(self, type):
171 self.set_byte(0, type)
173 def set_code(self, code):
174 self.set_byte(1, code)
176 def set_checksum(self, checksum):
177 self.set_word(2, checksum)
179############################################################################
180 def calculate_checksum(self):
181 #Initialize the checksum value to 0 to yield a correct calculation
182 self.set_checksum(0)
183 #Fetch the pseudo header from the IP6 parent packet
184 pseudo_header = self.parent().get_pseudo_header()
185 #Fetch the ICMP data
186 icmp_header = self.get_bytes()
187 #Build an array of bytes concatenating the pseudo_header, the ICMP header and the ICMP data (if present)
188 checksum_array = array.array('B')
189 checksum_array.extend(pseudo_header)
190 checksum_array.extend(icmp_header)
191 if (self.child()): 191 ↛ 195line 191 didn't jump to line 195, because the condition on line 191 was never false
192 checksum_array.extend(self.child().get_bytes())
194 #Compute the checksum over that array
195 self.set_checksum(self.compute_checksum(checksum_array))
197 def is_informational_message(self):
198 return self.icmp_messages[self.get_type()][self.MSG_TYPE_INDEX] == self.INFORMATIONAL_MESSAGE
200 def is_error_message(self):
201 return self.icmp_messages[self.get_type()][self.MSG_TYPE_INDEX] == self.ERROR_MESSAGE
203 def is_well_formed(self):
204 well_formed = True
206 #Check that the message type is known
207 well_formed &= self.get_type() in self.icmp_messages.keys()
209 #Check that the code is known (zero, if there are no codes defined)
210 code_dictionary = self.icmp_messages[self.get_type()][self.CODES_INDEX]
211 if (code_dictionary is None):
212 well_formed &= self.get_code() == 0
213 else:
214 well_formed &= self.get_code() in code_dictionary.keys()
216 return well_formed
218############################################################################
220 @classmethod
221 def Echo_Request(class_object, id, sequence_number, arbitrary_data = None):
222 return class_object.__build_echo_message(ICMP6.ECHO_REQUEST, id, sequence_number, arbitrary_data)
224 @classmethod
225 def Echo_Reply(class_object, id, sequence_number, arbitrary_data = None):
226 return class_object.__build_echo_message(ICMP6.ECHO_REPLY, id, sequence_number, arbitrary_data)
228 @classmethod
229 def __build_echo_message(class_object, type, id, sequence_number, arbitrary_data):
230 #Build ICMP6 header
231 icmp_packet = ICMP6()
232 icmp_packet.set_type(type)
233 icmp_packet.set_code(0)
235 #Pack ICMP payload
236 icmp_bytes = struct.pack('>H', id)
237 icmp_bytes += struct.pack('>H', sequence_number)
238 if (arbitrary_data is not None): 238 ↛ 240line 238 didn't jump to line 240, because the condition on line 238 was never false
239 icmp_bytes += array_tobytes(array.array('B', arbitrary_data))
240 icmp_payload = Data()
241 icmp_payload.set_data(icmp_bytes)
243 #Link payload to header
244 icmp_packet.contains(icmp_payload)
246 return icmp_packet
249############################################################################
250 @classmethod
251 def Destination_Unreachable(class_object, code, originating_packet_data = None):
252 unused_bytes = [0x00, 0x00, 0x00, 0x00]
253 return class_object.__build_error_message(ICMP6.DESTINATION_UNREACHABLE, code, unused_bytes, originating_packet_data)
255 @classmethod
256 def Packet_Too_Big(class_object, MTU, originating_packet_data = None):
257 MTU_bytes = struct.pack('!L', MTU)
258 return class_object.__build_error_message(ICMP6.PACKET_TOO_BIG, 0, MTU_bytes, originating_packet_data)
260 @classmethod
261 def Time_Exceeded(class_object, code, originating_packet_data = None):
262 unused_bytes = [0x00, 0x00, 0x00, 0x00]
263 return class_object.__build_error_message(ICMP6.TIME_EXCEEDED, code, unused_bytes, originating_packet_data)
265 @classmethod
266 def Parameter_Problem(class_object, code, pointer, originating_packet_data = None):
267 pointer_bytes = struct.pack('!L', pointer)
268 return class_object.__build_error_message(ICMP6.PARAMETER_PROBLEM, code, pointer_bytes, originating_packet_data)
270 @classmethod
271 def __build_error_message(class_object, type, code, data, originating_packet_data):
272 #Build ICMP6 header
273 icmp_packet = ICMP6()
274 icmp_packet.set_type(type)
275 icmp_packet.set_code(code)
277 #Pack ICMP payload
278 icmp_bytes = array_tobytes(array.array('B', data))
279 if (originating_packet_data is not None): 279 ↛ 281line 279 didn't jump to line 281, because the condition on line 279 was never false
280 icmp_bytes += array_tobytes(array.array('B', originating_packet_data))
281 icmp_payload = Data()
282 icmp_payload.set_data(icmp_bytes)
284 #Link payload to header
285 icmp_packet.contains(icmp_payload)
287 return icmp_packet
289############################################################################
291 @classmethod
292 def Neighbor_Solicitation(class_object, target_address):
293 return class_object.__build_neighbor_message(ICMP6.NEIGHBOR_SOLICITATION, target_address)
295 @classmethod
296 def Neighbor_Advertisement(class_object, target_address):
297 return class_object.__build_neighbor_message(ICMP6.NEIGHBOR_ADVERTISEMENT, target_address)
299 @classmethod
300 def __build_neighbor_message(class_object, msg_type, target_address):
301 #Build ICMP6 header
302 icmp_packet = ICMP6()
303 icmp_packet.set_type(msg_type)
304 icmp_packet.set_code(0)
306 # Flags + Reserved
307 icmp_bytes = array_tobytes(array.array('B', [0x00] * 4))
309 # Target Address: The IP address of the target of the solicitation.
310 # It MUST NOT be a multicast address.
311 icmp_bytes += array_tobytes(array.array('B', IP6_Address(target_address).as_bytes()))
313 icmp_payload = Data()
314 icmp_payload.set_data(icmp_bytes)
316 #Link payload to header
317 icmp_packet.contains(icmp_payload)
319 return icmp_packet
321############################################################################
323 def get_target_address(self):
324 return IP6_Address(self.child().get_bytes()[4:20])
326 def set_target_address(self, target_address):
327 address = IP6_Address(target_address)
328 payload_bytes = self.child().get_bytes()
329 payload_bytes[4:20] = address.get_bytes()
330 self.child().set_bytes(payload_bytes)
332 # 0 1 2 3 4 5 6 7
333 # +-+-+-+-+-+-+-+-+
334 # |R|S|O|reserved |
335 # +-+-+-+-+-+-+-+-+
337 def get_neighbor_advertisement_flags(self):
338 return self.child().get_byte(0)
340 def set_neighbor_advertisement_flags(self, flags):
341 self.child().set_byte(0, flags)
343 def get_router_flag(self):
344 return (self.get_neighbor_advertisement_flags() & 0x80) != 0
346 def set_router_flag(self, flag_value):
347 curr_flags = self.get_neighbor_advertisement_flags()
348 if flag_value:
349 curr_flags |= 0x80
350 else:
351 curr_flags &= ~0x80
352 self.set_neighbor_advertisement_flags(curr_flags)
354 def get_solicited_flag(self):
355 return (self.get_neighbor_advertisement_flags() & 0x40) != 0
357 def set_solicited_flag(self, flag_value):
358 curr_flags = self.get_neighbor_advertisement_flags()
359 if flag_value:
360 curr_flags |= 0x40
361 else:
362 curr_flags &= ~0x40
363 self.set_neighbor_advertisement_flags(curr_flags)
365 def get_override_flag(self):
366 return (self.get_neighbor_advertisement_flags() & 0x20) != 0
368 def set_override_flag(self, flag_value):
369 curr_flags = self.get_neighbor_advertisement_flags()
370 if flag_value:
371 curr_flags |= 0x20
372 else:
373 curr_flags &= ~0x20
374 self.set_neighbor_advertisement_flags(curr_flags)
376############################################################################
377 @classmethod
378 def Node_Information_Query(class_object, code, payload = None):
379 return class_object.__build_node_information_message(ICMP6.NODE_INFORMATION_QUERY, code, payload)
381 @classmethod
382 def Node_Information_Reply(class_object, code, payload = None):
383 return class_object.__build_node_information_message(ICMP6.NODE_INFORMATION_REPLY, code, payload)
385 @classmethod
386 def __build_node_information_message(class_object, type, code, payload = None):
387 #Build ICMP6 header
388 icmp_packet = ICMP6()
389 icmp_packet.set_type(type)
390 icmp_packet.set_code(code)
392 #Pack ICMP payload
393 qtype = 0
394 flags = 0
395 nonce = [0x00] * 8
397 icmp_bytes = struct.pack('>H', qtype)
398 icmp_bytes += struct.pack('>H', flags)
399 icmp_bytes += array_tobytes(array.array('B', nonce))
401 if payload is not None:
402 icmp_bytes += array_tobytes(array.array('B', payload))
404 icmp_payload = Data()
405 icmp_payload.set_data(icmp_bytes)
407 #Link payload to header
408 icmp_packet.contains(icmp_payload)
410 return icmp_packet
412 def get_qtype(self):
413 return self.child().get_word(0)
415 def set_qtype(self, qtype):
416 self.child().set_word(0, qtype)
418 def get_nonce(self):
419 return self.child().get_bytes()[4:12]
421 def set_nonce(self, nonce):
422 payload_bytes = self.child().get_bytes()
423 payload_bytes[4:12] = array.array('B', nonce)
424 self.child().set_bytes(payload_bytes)
426 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
427 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
428 # | unused |G|S|L|C|A|T|
429 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431 def get_flags(self):
432 return self.child().get_word(2)
434 def set_flags(self, flags):
435 self.child().set_word(2, flags)
437 def get_flag_T(self):
438 return (self.get_flags() & 0x0001) != 0
440 def set_flag_T(self, flag_value):
441 curr_flags = self.get_flags()
442 if flag_value:
443 curr_flags |= 0x0001
444 else:
445 curr_flags &= ~0x0001
446 self.set_flags(curr_flags)
448 def get_flag_A(self):
449 return (self.get_flags() & 0x0002) != 0
451 def set_flag_A(self, flag_value):
452 curr_flags = self.get_flags()
453 if flag_value:
454 curr_flags |= 0x0002
455 else:
456 curr_flags &= ~0x0002
457 self.set_flags(curr_flags)
459 def get_flag_C(self):
460 return (self.get_flags() & 0x0004) != 0
462 def set_flag_C(self, flag_value):
463 curr_flags = self.get_flags()
464 if flag_value:
465 curr_flags |= 0x0004
466 else:
467 curr_flags &= ~0x0004
468 self.set_flags(curr_flags)
470 def get_flag_L(self):
471 return (self.get_flags() & 0x0008) != 0
473 def set_flag_L(self, flag_value):
474 curr_flags = self.get_flags()
475 if flag_value:
476 curr_flags |= 0x0008
477 else:
478 curr_flags &= ~0x0008
479 self.set_flags(curr_flags)
481 def get_flag_S(self):
482 return (self.get_flags() & 0x0010) != 0
484 def set_flag_S(self, flag_value):
485 curr_flags = self.get_flags()
486 if flag_value:
487 curr_flags |= 0x0010
488 else:
489 curr_flags &= ~0x0010
490 self.set_flags(curr_flags)
492 def get_flag_G(self):
493 return (self.get_flags() & 0x0020) != 0
495 def set_flag_G(self, flag_value):
496 curr_flags = self.get_flags()
497 if flag_value:
498 curr_flags |= 0x0020
499 else:
500 curr_flags &= ~0x0020
501 self.set_flags(curr_flags)
503 def set_node_information_data(self, data):
504 payload_bytes = self.child().get_bytes()
505 payload_bytes[12:] = array.array('B', data)
506 self.child().set_bytes(payload_bytes)
508 def get_note_information_data(self):
509 return self.child().get_bytes()[12:]
511############################################################################
512 def get_echo_id(self):
513 return self.child().get_word(0)
515 def get_echo_sequence_number(self):
516 return self.child().get_word(2)
518 def get_echo_arbitrary_data(self):
519 return self.child().get_bytes()[4:]
521 def get_mtu(self):
522 return self.child().get_long(0)
524 def get_parm_problem_pointer(self):
525 return self.child().get_long(0)
527 def get_originating_packet_data(self):
528 return self.child().get_bytes()[4:]