프로그래밍/jpcap programming

Jpcap을 이용한 패킷 보내기 1

dp. 2011. 6. 2. 20:21
작성자 유창훈 

안녕하세요. 오늘은 패킷을 보내는 과정을 알아보도록 하겠습니다.
참고로 패킷을 보내는 예제는 jpcap 사이트 가시면 친절하게 있습니다. 
하지만 저는 그 방법을 사용할 것이 아니기때문에, 좀 다른 방법을 이용하도록 하겠습니다 . 

환경 셋팅 1,2 에서는 Jpcap library를 이용하여 랜카드를 선택하고, 우리가 원하는 패킷을 필터링 하는 방법을 알아보았습니다.
그러면 이제 마지막으로 우리가 원하는 형태의 패킷을 보내기만 한다면, 기본적인과정은 끝인 샘입니다. 

먼저 기존 방법부터 설명하도록 하겠습니다. 

많이들 사용하시는 winpcap에서는 sendPacket함수 인자에 전송 하고자하는 버퍼를  선언해 내용을 구성하고,  버퍼의 포인터와 길이를 넘겨줌으로써 패킷을 쉽게 보낼 수 있습니다. 이건 기존 레퍼런스도 많고 데이터형도 별로 따지지 않는데, Jpcap은 조금 다릅니다.

Jpcap은 winpcap에 비해 자바 특성인 클래스를 잘 살려서 각종 메소드와, 오브젝트를 사용합니다. 
데이터링크계층은 이더넷이라 가정하고, ICMP패킷을 예를 들어보겠습니다.

ICMP패킷을 보내기 위해서 친절하게 Jpcap은 ICMP패킷의 구조 자체를 멤버 변수로 구성해서 객체화하여 가지고 있습니다.
캡슐화되는 순서에 따라 제일먼저 ICMP 구성하고  IP헤더 구성해서  ICMP앞에 붙이고  ,  Ethernet 구성해서 IP헤더 앞에 붙이고,  ..... 이런 순서로 프로그래밍을 하게 됩니다. 
ICMP   =======>>    IP header + (ICMP) ==========>>  ( EthernetHeader + (IP_Header + (ICMP)))


프로그래밍 순서가 딱히 정해져 있는 것은 아니지만 이렇게 구현하는 것이 여러면에서 쉽습니다.


ICMP 구성
---------------------------------------------------------------------
ICMPPacket p=new ICMPPacket();
이렇게 선언하고   --  기존에 Jpcap이 다 만들어 놓은 틀을 사용하겠다!!!는뜻.
ICMPPacket 이라는 큰 틀은 p라는 이름으로 사용합니다.
<br />
  p.type=ICMPPacket.ICMP_TSTAMP;
p.seq=1000;
p.id=999;
p.orig_timestamp=123;
p.trans_timestamp=456;
p.recv_timestamp=789;
이렇게 값을 설정 할 수 있습니다. 
---------------------------------------------------------------------
IP헤더는 이렇게 
p.setIPv4Parameter(0,false,false,false,0,false,false,false,0,1010101,100,IPPacket.IPPROTO_ICMP,
InetAddress.getByName("www.yahoo.com"),InetAddress.getByName("www.amazon.com"));
p.data="data".getBytes();     

각 파라메터 순서로 그냥 셋팅할 수 있습니다. 참... 쉽죠?...그냥 어서 셋팅해 달라고 하는것 같네요. 
빨간줄 부분.... 희한한 놈입니다.. 

아시다시피  getByte()메소드는  앞에 스트링 "data"를 바이트로 변환 시켜줍니다. 이것을 packet 클래스의 byte형 배열인 data멤버에 맵핑시켜주게되면,  최소 60byte 이상의 공간을 할당받고  p에 데이터링크 계층에 해당하는 값을 할당 가능한 형태로 선언되게 됩니다. (데이터링크 계층의 헤더에 해당하는 값을 할당가능하게 해준다는 말이 더 정확할 것 같습니다)

소스분석만으로 볼때 순서상으로는 ICMP의 데이터부분인, ping을 할때 임의의 데이터를 선언하는 것으로 보이는데 , 참... 이리저리 돌려 테스트 해보니까 다양하게 쓰일 수도 있겠더라구요. 

한 예로 데이터 링크 영역의 값만을 셋팅하여 패킷을 보낼 때 사용되기도 합니다. 예를들어 이더넷에서 헤더만을 구성하여(목적지 맥주소, 소스 맥주소, 타입 or 길이)이렇게 만 구성하고 보낼 수 도 있다는 뜻입니다.


---------------------------------------------------------------------
다음 이더넷 해더를 구성하기 위해 "EthernetPacke" 이라는 클래스를 사용하고 
---------------------------------------------------------------------
  EthernetPacket ether=new EthernetPacket();
이렇게 선언하고   --  기존에 Jpcap이 다 만들어 놓은 틀을 사용하겠다!!!는뜻.

ether.frametype=EthernetPacket.ETHERTYPE_IP;
ether.src_mac=new byte[]{(byte)0,(byte)1,(byte)2,(byte)3,(byte)4,(byte)5};
ether.dst_mac=new byte[]{(byte)0,(byte)6,(byte)7,(byte)8,(byte)9,(byte)10};

이렇게 값을  기냥 셋팅해버리고

p.datalink=ether;
이렇게 ICMPPacket 클래스의  datalink 에 방금 구성한 이더넷 구조를  맵핑시킵니다. 

sender.sendPacket(p);
그리고 이렇게 보냅니다. 

즉, 
1. 전체 Packet구조인 ICMPPacket 구조를 사용하겠다 선언하고,   ICMP 멤버변수를 셋팅한다.
2. IP의 data부분인 ICMP구조가 완성되었으므로, 캡슐화순서에 따라 IP헤더부분을 작성한다.
3. getbyte();메소드를 통해 전체 ICMPPacket로 선언된 p의 데이터링크 계층(이더넷헤더)의 해당하는 값을 할당할 수 있음을 선언한다.
4. 이더넷의 데이터 부분인 IP헤더 IP데이터(ICMP)가 다 작성되었으므로 이더넷 헤더를 작성한다.
5. 헤더 작성후, 이더넷 헤더를 기존의 ICMPPacket에 붙인다
6. 보낸다.

=========full source ================================================
SendICMP.java


import jpcap.*;
import jpcap.packet.EthernetPacket;
import jpcap.packet.ICMPPacket;
import jpcap.packet.IPPacket;

class SendICMP
{
public static void main(String[] args) throws java.io.IOException{
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
if(args.length<1){
System.out.println("Usage: java SentICMP <device index (e.g., 0, 1..)>");
for(int i=0;i<devices.length;i++)
System.out.println(i+":"+devices[i].name+"("+devices[i].description+")");
System.exit(0);
}
int index=Integer.parseInt(args[0]);
JpcapSender sender=JpcapSender.openDevice(devices[index]);

ICMPPacket p=new ICMPPacket();
p.type=ICMPPacket.ICMP_TSTAMP;
p.seq=1000;
p.id=999;
p.orig_timestamp=123;
p.trans_timestamp=456;
p.recv_timestamp=789;
p.setIPv4Parameter(0,false,false,false,0,false,false,false,0,1010101,100,IPPacket.IPPROTO_ICMP,
InetAddress.getByName("www.yahoo.com"),InetAddress.getByName("www.amazon.com"));
p.data="data".getBytes();

EthernetPacket ether=new EthernetPacket();
ether.frametype=EthernetPacket.ETHERTYPE_IP;
ether.src_mac=new byte[]{(byte)0,(byte)1,(byte)2,(byte)3,(byte)4,(byte)5};
ether.dst_mac=new byte[]{(byte)0,(byte)6,(byte)7,(byte)8,(byte)9,(byte)10};
p.datalink=ether;

//for(int i=0;i<10;i++)
sender.sendPacket(p);
}
}
===============================================================

여기까지가 기존의 Jpcap에서 지원하는 방법으로 ICMP패킷을 보내는 방법입니다.  내용이 길어졌네요,  

이제 우리가 구현할 방법을 설명할 차례인데....너무 피곤하네요.. 3시인지라... 
오늘 다쓰려고했는데 너무 많아졌습니다. 

일단 자고 일어나서 올리도록 하겠습니다.