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# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
8import array
9import struct
11from impacket.ImpactPacket import Header, Data, array_tobytes
12from impacket.IP6_Address import IP6_Address
15class ICMP6(Header):
16 #IP Protocol number for ICMP6
17 IP_PROTOCOL_NUMBER = 58
18 protocol = IP_PROTOCOL_NUMBER #ImpactDecoder uses the constant "protocol" as the IP Protocol Number
20 #Size of ICMP6 header (excluding payload)
21 HEADER_SIZE = 4
23 #ICMP6 Message Type numbers
24 DESTINATION_UNREACHABLE = 1
25 PACKET_TOO_BIG = 2
26 TIME_EXCEEDED = 3
27 PARAMETER_PROBLEM = 4
28 ECHO_REQUEST = 128
29 ECHO_REPLY = 129
30 ROUTER_SOLICITATION = 133
31 ROUTER_ADVERTISEMENT = 134
32 NEIGHBOR_SOLICITATION = 135
33 NEIGHBOR_ADVERTISEMENT = 136
34 REDIRECT_MESSAGE = 137
35 NODE_INFORMATION_QUERY = 139
36 NODE_INFORMATION_REPLY = 140
38 #Destination Unreachable codes
39 NO_ROUTE_TO_DESTINATION = 0
40 ADMINISTRATIVELY_PROHIBITED = 1
41 BEYOND_SCOPE_OF_SOURCE_ADDRESS = 2
42 ADDRESS_UNREACHABLE = 3
43 PORT_UNREACHABLE = 4
44 SOURCE_ADDRESS_FAILED_INGRESS_EGRESS_POLICY = 5
45 REJECT_ROUTE_TO_DESTINATION = 6
47 #Time Exceeded codes
48 HOP_LIMIT_EXCEEDED_IN_TRANSIT = 0
49 FRAGMENT_REASSEMBLY_TIME_EXCEEDED = 1
51 #Parameter problem codes
52 ERRONEOUS_HEADER_FIELD_ENCOUNTERED = 0
53 UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED = 1
54 UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED = 2
56 #Node Information codes
57 NODE_INFORMATION_QUERY_IPV6 = 0
58 NODE_INFORMATION_QUERY_NAME_OR_EMPTY = 1
59 NODE_INFORMATION_QUERY_IPV4 = 2
60 NODE_INFORMATION_REPLY_SUCCESS = 0
61 NODE_INFORMATION_REPLY_REFUSED = 1
62 NODE_INFORMATION_REPLY_UNKNOWN_QTYPE = 2
64 #Node Information qtypes
65 NODE_INFORMATION_QTYPE_NOOP = 0
66 NODE_INFORMATION_QTYPE_UNUSED = 1
67 NODE_INFORMATION_QTYPE_NODENAME = 2
68 NODE_INFORMATION_QTYPE_NODEADDRS = 3
69 NODE_INFORMATION_QTYPE_IPv4ADDRS = 4
71 #ICMP Message semantic types (error or informational)
72 ERROR_MESSAGE = 0
73 INFORMATIONAL_MESSAGE = 1
75 #ICMP message dictionary - specifying text descriptions and valid message codes
76 #Key: ICMP message number
77 #Data: Tuple ( Message Type (error/informational), Text description, Codes dictionary (can be None) )
78 #Codes dictionary
79 #Key: Code number
80 #Data: Text description
82 #ICMP message dictionary tuple indexes
83 MSG_TYPE_INDEX = 0
84 DESCRIPTION_INDEX = 1
85 CODES_INDEX = 2
87 icmp_messages = {
88 DESTINATION_UNREACHABLE : (ERROR_MESSAGE, "Destination unreachable",
89 { NO_ROUTE_TO_DESTINATION : "No route to destination",
90 ADMINISTRATIVELY_PROHIBITED : "Administratively prohibited",
91 BEYOND_SCOPE_OF_SOURCE_ADDRESS : "Beyond scope of source address",
92 ADDRESS_UNREACHABLE : "Address unreachable",
93 PORT_UNREACHABLE : "Port unreachable",
94 SOURCE_ADDRESS_FAILED_INGRESS_EGRESS_POLICY : "Source address failed ingress/egress policy",
95 REJECT_ROUTE_TO_DESTINATION : "Reject route to destination"
96 }),
97 PACKET_TOO_BIG : (ERROR_MESSAGE, "Packet too big", None),
98 TIME_EXCEEDED : (ERROR_MESSAGE, "Time exceeded",
99 {HOP_LIMIT_EXCEEDED_IN_TRANSIT : "Hop limit exceeded in transit",
100 FRAGMENT_REASSEMBLY_TIME_EXCEEDED : "Fragment reassembly time exceeded"
101 }),
102 PARAMETER_PROBLEM : (ERROR_MESSAGE, "Parameter problem",
103 {
104 ERRONEOUS_HEADER_FIELD_ENCOUNTERED : "Erroneous header field encountered",
105 UNRECOGNIZED_NEXT_HEADER_TYPE_ENCOUNTERED : "Unrecognized Next Header type encountered",
106 UNRECOGNIZED_IPV6_OPTION_ENCOUNTERED : "Unrecognized IPv6 Option Encountered"
107 }),
108 ECHO_REQUEST : (INFORMATIONAL_MESSAGE, "Echo request", None),
109 ECHO_REPLY : (INFORMATIONAL_MESSAGE, "Echo reply", None),
110 ROUTER_SOLICITATION : (INFORMATIONAL_MESSAGE, "Router Solicitation", None),
111 ROUTER_ADVERTISEMENT : (INFORMATIONAL_MESSAGE, "Router Advertisement", None),
112 NEIGHBOR_SOLICITATION : (INFORMATIONAL_MESSAGE, "Neighbor Solicitation", None),
113 NEIGHBOR_ADVERTISEMENT : (INFORMATIONAL_MESSAGE, "Neighbor Advertisement", None),
114 REDIRECT_MESSAGE : (INFORMATIONAL_MESSAGE, "Redirect Message", None),
115 NODE_INFORMATION_QUERY: (INFORMATIONAL_MESSAGE, "Node Information Query", None),
116 NODE_INFORMATION_REPLY: (INFORMATIONAL_MESSAGE, "Node Information Reply", None),
117 }
122############################################################################
123 def __init__(self, buffer = None):
124 Header.__init__(self, self.HEADER_SIZE)
125 if (buffer):
126 self.load_header(buffer)
128 def get_header_size(self):
129 return self.HEADER_SIZE
131 def get_ip_protocol_number(self):
132 return self.IP_PROTOCOL_NUMBER
134 def __str__(self):
135 type = self.get_type()
136 code = self.get_code()
137 checksum = self.get_checksum()
139 s = "ICMP6 - Type: " + str(type) + " - " + self.__get_message_description() + "\n"
140 s += "Code: " + str(code)
141 if (self.__get_code_description() != ""):
142 s += " - " + self.__get_code_description()
143 s += "\n"
144 s += "Checksum: " + str(checksum) + "\n"
145 return s
147 def __get_message_description(self):
148 return self.icmp_messages[self.get_type()][self.DESCRIPTION_INDEX]
150 def __get_code_description(self):
151 code_dictionary = self.icmp_messages[self.get_type()][self.CODES_INDEX]
152 if (code_dictionary is None):
153 return ""
154 else:
155 return code_dictionary[self.get_code()]
157############################################################################
158 def get_type(self):
159 return (self.get_byte(0))
161 def get_code(self):
162 return (self.get_byte(1))
164 def get_checksum(self):
165 return (self.get_word(2))
167############################################################################
168 def set_type(self, type):
169 self.set_byte(0, type)
171 def set_code(self, code):
172 self.set_byte(1, code)
174 def set_checksum(self, checksum):
175 self.set_word(2, checksum)
177############################################################################
178 def calculate_checksum(self):
179 #Initialize the checksum value to 0 to yield a correct calculation
180 self.set_checksum(0)
181 #Fetch the pseudo header from the IP6 parent packet
182 pseudo_header = self.parent().get_pseudo_header()
183 #Fetch the ICMP data
184 icmp_header = self.get_bytes()
185 #Build an array of bytes concatenating the pseudo_header, the ICMP header and the ICMP data (if present)
186 checksum_array = array.array('B')
187 checksum_array.extend(pseudo_header)
188 checksum_array.extend(icmp_header)
189 if (self.child()): 189 ↛ 193line 189 didn't jump to line 193, because the condition on line 189 was never false
190 checksum_array.extend(self.child().get_bytes())
192 #Compute the checksum over that array
193 self.set_checksum(self.compute_checksum(checksum_array))
195 def is_informational_message(self):
196 return self.icmp_messages[self.get_type()][self.MSG_TYPE_INDEX] == self.INFORMATIONAL_MESSAGE
198 def is_error_message(self):
199 return self.icmp_messages[self.get_type()][self.MSG_TYPE_INDEX] == self.ERROR_MESSAGE
201 def is_well_formed(self):
202 well_formed = True
204 #Check that the message type is known
205 well_formed &= self.get_type() in self.icmp_messages.keys()
207 #Check that the code is known (zero, if there are no codes defined)
208 code_dictionary = self.icmp_messages[self.get_type()][self.CODES_INDEX]
209 if (code_dictionary is None):
210 well_formed &= self.get_code() == 0
211 else:
212 well_formed &= self.get_code() in code_dictionary.keys()
214 return well_formed
216############################################################################
218 @classmethod
219 def Echo_Request(class_object, id, sequence_number, arbitrary_data = None):
220 return class_object.__build_echo_message(ICMP6.ECHO_REQUEST, id, sequence_number, arbitrary_data)
222 @classmethod
223 def Echo_Reply(class_object, id, sequence_number, arbitrary_data = None):
224 return class_object.__build_echo_message(ICMP6.ECHO_REPLY, id, sequence_number, arbitrary_data)
226 @classmethod
227 def __build_echo_message(class_object, type, id, sequence_number, arbitrary_data):
228 #Build ICMP6 header
229 icmp_packet = ICMP6()
230 icmp_packet.set_type(type)
231 icmp_packet.set_code(0)
233 #Pack ICMP payload
234 icmp_bytes = struct.pack('>H', id)
235 icmp_bytes += struct.pack('>H', sequence_number)
236 if (arbitrary_data is not None): 236 ↛ 238line 236 didn't jump to line 238, because the condition on line 236 was never false
237 icmp_bytes += array_tobytes(array.array('B', arbitrary_data))
238 icmp_payload = Data()
239 icmp_payload.set_data(icmp_bytes)
241 #Link payload to header
242 icmp_packet.contains(icmp_payload)
244 return icmp_packet
247############################################################################
248 @classmethod
249 def Destination_Unreachable(class_object, code, originating_packet_data = None):
250 unused_bytes = [0x00, 0x00, 0x00, 0x00]
251 return class_object.__build_error_message(ICMP6.DESTINATION_UNREACHABLE, code, unused_bytes, originating_packet_data)
253 @classmethod
254 def Packet_Too_Big(class_object, MTU, originating_packet_data = None):
255 MTU_bytes = struct.pack('!L', MTU)
256 return class_object.__build_error_message(ICMP6.PACKET_TOO_BIG, 0, MTU_bytes, originating_packet_data)
258 @classmethod
259 def Time_Exceeded(class_object, code, originating_packet_data = None):
260 unused_bytes = [0x00, 0x00, 0x00, 0x00]
261 return class_object.__build_error_message(ICMP6.TIME_EXCEEDED, code, unused_bytes, originating_packet_data)
263 @classmethod
264 def Parameter_Problem(class_object, code, pointer, originating_packet_data = None):
265 pointer_bytes = struct.pack('!L', pointer)
266 return class_object.__build_error_message(ICMP6.PARAMETER_PROBLEM, code, pointer_bytes, originating_packet_data)
268 @classmethod
269 def __build_error_message(class_object, type, code, data, originating_packet_data):
270 #Build ICMP6 header
271 icmp_packet = ICMP6()
272 icmp_packet.set_type(type)
273 icmp_packet.set_code(code)
275 #Pack ICMP payload
276 icmp_bytes = array_tobytes(array.array('B', data))
277 if (originating_packet_data is not None): 277 ↛ 279line 277 didn't jump to line 279, because the condition on line 277 was never false
278 icmp_bytes += array_tobytes(array.array('B', originating_packet_data))
279 icmp_payload = Data()
280 icmp_payload.set_data(icmp_bytes)
282 #Link payload to header
283 icmp_packet.contains(icmp_payload)
285 return icmp_packet
287############################################################################
289 @classmethod
290 def Neighbor_Solicitation(class_object, target_address):
291 return class_object.__build_neighbor_message(ICMP6.NEIGHBOR_SOLICITATION, target_address)
293 @classmethod
294 def Neighbor_Advertisement(class_object, target_address):
295 return class_object.__build_neighbor_message(ICMP6.NEIGHBOR_ADVERTISEMENT, target_address)
297 @classmethod
298 def __build_neighbor_message(class_object, msg_type, target_address):
299 #Build ICMP6 header
300 icmp_packet = ICMP6()
301 icmp_packet.set_type(msg_type)
302 icmp_packet.set_code(0)
304 # Flags + Reserved
305 icmp_bytes = array_tobytes(array.array('B', [0x00] * 4))
307 # Target Address: The IP address of the target of the solicitation.
308 # It MUST NOT be a multicast address.
309 icmp_bytes += array_tobytes(array.array('B', IP6_Address(target_address).as_bytes()))
311 icmp_payload = Data()
312 icmp_payload.set_data(icmp_bytes)
314 #Link payload to header
315 icmp_packet.contains(icmp_payload)
317 return icmp_packet
319############################################################################
321 def get_target_address(self):
322 return IP6_Address(self.child().get_bytes()[4:20])
324 def set_target_address(self, target_address):
325 address = IP6_Address(target_address)
326 payload_bytes = self.child().get_bytes()
327 payload_bytes[4:20] = address.get_bytes()
328 self.child().set_bytes(payload_bytes)
330 # 0 1 2 3 4 5 6 7
331 # +-+-+-+-+-+-+-+-+
332 # |R|S|O|reserved |
333 # +-+-+-+-+-+-+-+-+
335 def get_neighbor_advertisement_flags(self):
336 return self.child().get_byte(0)
338 def set_neighbor_advertisement_flags(self, flags):
339 self.child().set_byte(0, flags)
341 def get_router_flag(self):
342 return (self.get_neighbor_advertisement_flags() & 0x80) != 0
344 def set_router_flag(self, flag_value):
345 curr_flags = self.get_neighbor_advertisement_flags()
346 if flag_value:
347 curr_flags |= 0x80
348 else:
349 curr_flags &= ~0x80
350 self.set_neighbor_advertisement_flags(curr_flags)
352 def get_solicited_flag(self):
353 return (self.get_neighbor_advertisement_flags() & 0x40) != 0
355 def set_solicited_flag(self, flag_value):
356 curr_flags = self.get_neighbor_advertisement_flags()
357 if flag_value:
358 curr_flags |= 0x40
359 else:
360 curr_flags &= ~0x40
361 self.set_neighbor_advertisement_flags(curr_flags)
363 def get_override_flag(self):
364 return (self.get_neighbor_advertisement_flags() & 0x20) != 0
366 def set_override_flag(self, flag_value):
367 curr_flags = self.get_neighbor_advertisement_flags()
368 if flag_value:
369 curr_flags |= 0x20
370 else:
371 curr_flags &= ~0x20
372 self.set_neighbor_advertisement_flags(curr_flags)
374############################################################################
375 @classmethod
376 def Node_Information_Query(class_object, code, payload = None):
377 return class_object.__build_node_information_message(ICMP6.NODE_INFORMATION_QUERY, code, payload)
379 @classmethod
380 def Node_Information_Reply(class_object, code, payload = None):
381 return class_object.__build_node_information_message(ICMP6.NODE_INFORMATION_REPLY, code, payload)
383 @classmethod
384 def __build_node_information_message(class_object, type, code, payload = None):
385 #Build ICMP6 header
386 icmp_packet = ICMP6()
387 icmp_packet.set_type(type)
388 icmp_packet.set_code(code)
390 #Pack ICMP payload
391 qtype = 0
392 flags = 0
393 nonce = [0x00] * 8
395 icmp_bytes = struct.pack('>H', qtype)
396 icmp_bytes += struct.pack('>H', flags)
397 icmp_bytes += array_tobytes(array.array('B', nonce))
399 if payload is not None:
400 icmp_bytes += array_tobytes(array.array('B', payload))
402 icmp_payload = Data()
403 icmp_payload.set_data(icmp_bytes)
405 #Link payload to header
406 icmp_packet.contains(icmp_payload)
408 return icmp_packet
410 def get_qtype(self):
411 return self.child().get_word(0)
413 def set_qtype(self, qtype):
414 self.child().set_word(0, qtype)
416 def get_nonce(self):
417 return self.child().get_bytes()[4:12]
419 def set_nonce(self, nonce):
420 payload_bytes = self.child().get_bytes()
421 payload_bytes[4:12] = array.array('B', nonce)
422 self.child().set_bytes(payload_bytes)
424 # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
425 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
426 # | unused |G|S|L|C|A|T|
427 # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
429 def get_flags(self):
430 return self.child().get_word(2)
432 def set_flags(self, flags):
433 self.child().set_word(2, flags)
435 def get_flag_T(self):
436 return (self.get_flags() & 0x0001) != 0
438 def set_flag_T(self, flag_value):
439 curr_flags = self.get_flags()
440 if flag_value:
441 curr_flags |= 0x0001
442 else:
443 curr_flags &= ~0x0001
444 self.set_flags(curr_flags)
446 def get_flag_A(self):
447 return (self.get_flags() & 0x0002) != 0
449 def set_flag_A(self, flag_value):
450 curr_flags = self.get_flags()
451 if flag_value:
452 curr_flags |= 0x0002
453 else:
454 curr_flags &= ~0x0002
455 self.set_flags(curr_flags)
457 def get_flag_C(self):
458 return (self.get_flags() & 0x0004) != 0
460 def set_flag_C(self, flag_value):
461 curr_flags = self.get_flags()
462 if flag_value:
463 curr_flags |= 0x0004
464 else:
465 curr_flags &= ~0x0004
466 self.set_flags(curr_flags)
468 def get_flag_L(self):
469 return (self.get_flags() & 0x0008) != 0
471 def set_flag_L(self, flag_value):
472 curr_flags = self.get_flags()
473 if flag_value:
474 curr_flags |= 0x0008
475 else:
476 curr_flags &= ~0x0008
477 self.set_flags(curr_flags)
479 def get_flag_S(self):
480 return (self.get_flags() & 0x0010) != 0
482 def set_flag_S(self, flag_value):
483 curr_flags = self.get_flags()
484 if flag_value:
485 curr_flags |= 0x0010
486 else:
487 curr_flags &= ~0x0010
488 self.set_flags(curr_flags)
490 def get_flag_G(self):
491 return (self.get_flags() & 0x0020) != 0
493 def set_flag_G(self, flag_value):
494 curr_flags = self.get_flags()
495 if flag_value:
496 curr_flags |= 0x0020
497 else:
498 curr_flags &= ~0x0020
499 self.set_flags(curr_flags)
501 def set_node_information_data(self, data):
502 payload_bytes = self.child().get_bytes()
503 payload_bytes[12:] = array.array('B', data)
504 self.child().set_bytes(payload_bytes)
506 def get_note_information_data(self):
507 return self.child().get_bytes()[12:]
509############################################################################
510 def get_echo_id(self):
511 return self.child().get_word(0)
513 def get_echo_sequence_number(self):
514 return self.child().get_word(2)
516 def get_echo_arbitrary_data(self):
517 return self.child().get_bytes()[4:]
519 def get_mtu(self):
520 return self.child().get_long(0)
522 def get_parm_problem_pointer(self):
523 return self.child().get_long(0)
525 def get_originating_packet_data(self):
526 return self.child().get_bytes()[4:]