2012-04-04 5 views
0

파이썬에서 ICMP 기반 Traceroute를 구현하려고합니다. 매우 유용한 가이드 (https://blogs.oracle.com/ksplice/entry/learning_by_doing_writing_your)를 발견하여 UDP 기반 Traceroute를 만들 수있게하여 수정이 필요합니다. 그러나 나는 주변을 둘러 보았고 send 소켓을 변경하고 작동시키는 데 문제가있다. 아무도 나를 도울 수 없습니까?Python에서 ICMP traceroute 만들기

#!/usr/bin/python 

import socket 

def main(dest_name): 
    dest_addr = socket.gethostbyname(dest_name) 
    port = 33434 
    max_hops = 30 
    icmp = socket.getprotobyname('icmp') 
    udp = socket.getprotobyname('udp') 
    ttl = 1 
    while True: 
     recv_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp) 
     send_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, udp) 
     send_socket.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl) 
     recv_socket.bind(("", port)) 
     send_socket.sendto("", (dest_name, port)) 
     curr_addr = None 
     curr_name = None 
     try: 
      _, curr_addr = recv_socket.recvfrom(512) 
      curr_addr = curr_addr[0] 
      try: 
       curr_name = socket.gethostbyaddr(curr_addr)[0] 
      except socket.error: 
       curr_name = curr_addr 
     except socket.error: 
      pass 
     finally: 
      send_socket.close() 
      recv_socket.close() 

     if curr_addr is not None: 
      curr_host = "%s (%s)" % (curr_name, curr_addr) 
     else: 
      curr_host = "*" 
     print "%d\t%s" % (ttl, curr_host) 

     ttl += 1 
     if curr_addr == dest_addr or ttl > max_hops: 
      break 

if __name__ == "__main__": 
    main('google.com') 
+0

루트로 실행하지 않으면 ** 수신 ** 소켓 때문에'조작이 허용되지 않습니다. '가 발생합니다. –

+0

어떤 플랫폼에서 실행하고 있습니까? 실제로 언급 한 기사에서는'원시 소켓에는 루트 권한이 필요하기 때문에 traceroute는 일반적으로 setuid입니다. 우리의 목적을 위해서, 우리는 root로 스크립트를 실행할 수 있습니다 :'. 그러나 예를 들어 RHEL5에서'traceroute'은 setuid 루트가 아닙니다. (또한 http://traceroute.sourceforge.net/도 참조하십시오.) –

+0

우분투에서 실행 중이며 루트로 실행 중일 때 런타임 문제로 실행시 문제가 없습니다. – Jamesla

답변

-2

필자는 scapy를 사용할 수 없기 때문에 끝내었다.

+0

을 보여줌으로써 다른 사람들을 도와주세요 다른 라이브러리 (scapy)를 사용하더라도. – KillianDS

2

scapy (좋은 모듈 임에도 불구하고)를 선택한 이유는 확실하지 않습니다. 확실히 python 만 사용하면 가능합니다. ICMP 패킷을 보내려면 recv_socket을 보내면됩니다. 이 소켓을 보내려면 먼저 ICMP 패킷을 만들어야합니다.

그러나 원하는 것은 ICMP 소켓을 통해 UDP 패킷을 보내는 것입니다. 생각하는 것처럼 작동하지 않습니다.

우선, Linux 커널에 SOCK_DGRAM을 IPPROTO_ICMP : ICMP sockets (linux)과 함께 사용할 수있는 패치가 있다고 가정 해 보겠습니다. 나는 이것을 시험하지 않았다.

그러나 일반적으로 소켓 플래그 조합은 작동하지 않습니다. 이는 ICMP 소켓에 ICMP 헤더가 필요하기 때문입니다. send_socket과 마찬가지로 recv_socket을 통해 빈 문자열을 보내려면 커널이 패킷을 삭제합니다. 또한 ICMP 헤더를 통해 UDP 헤더를 계층화하는 경우 수신 시스템은 수신 된 ICMP 헤더에만 반응하여 UDP 헤더를 ICMP에 추가 된 데이터 이상으로 취급합니다. 사실, 당신에게 보내는 ICMP 답장에서, 원격 시스템은 UDP 헤더를 처음에 당신이 보낸 데이터로 간단하게 포함시킬 것입니다.

send_socket을 통해 빈 문자열을 보낼 수있는 이유는 커널이 UDP 헤더를 생성했기 때문이며 그 UDP 소켓을 통해 보내는 것은 단순히 UDP 헤더에 데이터로 추가됩니다. 그것은 ICMP 소켓과는 다르다. 필자가 작성한 것처럼 icmp 헤더를 만들어이 소켓에 보내야합니다.

실제로 UDP "핑"에서 일어나는 일은 다음과 같습니다. UDP 포트를 통해 UDP 포트를 통해 원격 시스템에 보내지 만, 열지 않은 포트는 대상 포트로 사용합니다. 이렇게하면 원격 시스템 (유형 3, 코드 3)에서 ICMP 응답이 반환됩니다. 이 시점에서 ICMP 등록 소켓이 필요합니다. ICMP 등록 소켓은 루트 (또는 Windows의 관리자) 권한이 필요한 ICMP 응답을 처리합니다. ICMP 에코 헤더를 만들려면

은 매우 쉽습니다 :

import struct 
icmp = struct.pack(">BBHHH", 8, 0, 0, 0, 0) 
icmp = struct.pack(">BBHHH", 8, 0, checksum(icmp), 0, 0) 

몇 가지가이 헤더에 주목. 첫째, 인자 4와 인자 5는 "Identifier"와 "Sequence Number"필드입니다. 일반적으로 식별자는 프로세스 ID로 설정되는 반면 시퀀스 번호는 1로 시작하여 응답에 대한 전송 순서를 추적 할 수 있습니다. 이 예제에서는 일러스트레이션을 위해 숫자를 0으로 설정했습니다 (비록 신경 쓰지 않는다면이 방법을 사용하는 것이 정상입니다).

두 번째로, 내가 언급 한 체크섬 (icmp) 기능을 참조하십시오. 이 작업을 수행하는 코드에 액세스 할 수 있다고 가정합니다. 이것은 1의 보수 체크섬으로 알려져 있으며 RFC 1071에 따라 비준됩니다. 제대로 계산되지 않으면 수신 시스템이 패킷을 ICMP 소켓 처리기로 전달하지 않을 수 있기 때문에 체크섬은 중요합니다.

셋째, 처음에 체크섬이 0 인 헤더를 만든 이유는 커널이 실제 체크섬이 만들어지고 헤더에 다시 추가되기 전에 해당 필드의 체크섬 결과가 0이되기 때문입니다.

구조체 모듈은 "압축 된"이진 데이터를 처리하기 때문에 비트 이동 및 비트 연산자를 익히는 것이 좋습니다. 당신은 당신이 비트 필드에서 특정 비트를 추출해야 할 필요가있는 경우, 특히 겹치지 않을 수도있는 원시 소켓으로 더 나아갈 때 이것을 필요로 할 것입니다.

0     1     2     3 
    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 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
0 |Version| IHL |Type of Service|   Total Length   | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
4 |   Identification  |Flags|  Fragment Offset | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
8 | Time to Live | Protocol |   Header Checksum  | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
12 |      Source Address       | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
16 |     Destination Address      | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
20 |     Options     | Padding | 
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 

먼저 길이, 4 바이트에 유의해야한다, 당신은 소스 필드에 IP 주소 192.168.1.10를 삽입하고 싶었 가정 예를 들어, 다음은 IP 헤더입니다. 제대로 구조체에이를 삽입하려면 다음을 수행해야합니다 (바로이 분야, 예를 들어, 헤더의 휴식되지 않음) :

struct.pack(">I", 192 << 24| 168 << 16| 1 << 8| 10) 

을이이 분야, 3232235786L에 대한 적절한 정수를 추가 하.

(일부 독자는 struct.pack ("> BBBB", 192, 168, 1, 10)을 사용하면 동일한 결과가 발생할 수 있음을 지적 할 수도 있습니다.이 사용 사례에서는 작동하지만 일반적으로 올바르지 않습니다 이 경우 IP 주소가 바이트 지향이기 때문에 작동하지만 체크섬 필드와 같이 바이트 지향이 아닌 필드는 체크섬의 결과 값이 255보다 크므로 실패합니다. 16 비트 값이 될 것으로 예상되므로 일반적으로 프로토콜 필드에서 요구하는 정확한 비트를 사용하지 마십시오.

비트 이동 및 연산을 학습하는 또 다른 예제로 VER 필드 IP 헤더의. 이것은 니블 크기 필드, 즉 4 비트입니다. 이를 추출하기 위해, 당신은 예를 들어 다음 (가정 IHL가 제로) 할 것 :

# Assume ip[] already has a full IP header. 
ver = struct.unpack("!B", ip[0])[0] 

# This produces the integer 4, which is required for sending IPV4 
ver >> 4 

를 간단히 말해서, 나는 오른쪽으로 이동 1 바이트 값이 아래로 4 비트했습니다. 이 하향 변속에서 이전 값과 비교하여 새로운 값은 이제 다음과 같습니다

# OLD VALUE IN BINARY; value is 64 
# 01000000 

# NEW VALUE IN BINARY; value is 4 
# 00000100 

그것은 아닙니다 무엇 인 (64)을 초래할 것 원시 이진 값을 확인,이 변화하지 않고, 점에 유의하는 것이 중요합니다 예상했다. 역은 "팩"한 바이트의 상위 4 비트 값 4로하기 위해서는, 이렇게이 오른쪽 방향을 가리키는

# New value will be 64, or binary 01000000 
x = 4 << 4 

희망.

0

이전 질문이지만, 최근에이 점을 명확히하기 위해 또 다른 요점을 추가했습니다.

소켓을 사용하여 파이썬 버전의 ICMP traceroute를 작성할 수 있습니다. 해결하는 두 가지 중요한 문제는 다음과 같습니다 정확한 검사와 ICMP 헤더를 생성

  1. sockopts
  2. 첫 번째 문제를 들어

를 사용하여 소켓의 TTL을 설정

  • (유진 위의 상태로) 완벽하게 작동하는 pyping 모듈의 훌륭한 예가 있습니다. 그것은 내가 traceroute 유틸리티를 작성할 때 사용했던 것이다.

    'TTL'은 당신이

    을 설정하고있는 TTL의 정수 값

    current_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, 
               socket.getprotobyname("icmp")) 
    current_socket.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl) 
    

    그런 다음이 보내기/RECV을하고 단지 문제 :로 두 번째를 들어

    , 그것은으로 쉽게 및 TTL을 증가시키는 제어 구조에서 리턴 패킷의 타입/코드를 관찰하는 단계를 포함한다.

    필자는 pyping 모듈에 이미 쓰여진 내용을 사용하여 내 헤더 팩/언팩을 모델링했습니다. 중간 홉의 경우 코드가 0 인 헤더 유형 = 11을 찾고 대상에 도달 한 경우 헤더 유형 = 0을 찾습니다.

    Scapy는 정상적으로 작동하지만 속도가 느려지고 외부 종속성이 추가됩니다. 나는 소켓을 사용하여 AS-lookup (RADb를 통한)과 geolocation을 사용하여 오후에 강력한 traceroute를 작성할 수있었습니다.