오늘도 누구 보여줄라고가 아니라 나 보려고 libnet을 이용한 arp 패킷 전송 예제코드를 간단히 분석합니다.
libpcap은 사용자도 많고 덩치도 크다보니 여기저기 자료가 많지만 libnet은 배포시에 딸려나오는
예제코드와 참고문서가 아니면 어디서 자료 구할데도 없지요.
에휴 기한도 지난 라우터 짠다고 내가 늙는다 늙어.
/*
* 아 몰라 너무 길어 귀찮아
*/
#if (HAVE_CONFIG_H)
#if ((_WIN32) && !(__CYGWIN__))
#include "../include/win32/config.h"
#else
#include "../include/config.h"
#endif
#endif
#include "./libnet_test.h"
딱히 설명할 필요는 없는 부분. 헤더파일도 뭐 그리 대단한 내용이 있는건 아니다.
int
main(int argc, char *argv[])
{
int c;
u_int32_t i;
libnet_t *l;
libnet_ptag_t t;
char *device = NULL;
u_int8_t *packet;
u_int32_t packet_s;
char errbuf[LIBNET_ERRBUF_SIZE];
printf("libnet 1.1 packet shaping: ARP[link -- autobuilding ethernet]\n");
if (argc > 1) // 명령 인자가 있다면
{
device = argv[1]; // 인자로 받은 장치를 쓰자.
}
l = libnet_init(
LIBNET_LINK_ADV, /* injection type */
device, /* network interface */
errbuf); /* errbuf */
libnet을 초기화한다.
injection type : 주입 모드? 직역하면 요상한 말이 돼버리는데, libnet의 처리를 어느 층에서 할 것인지를 결정한다.
LIBNET_LINK / LIBNET_LINK_ADV - 링크 계층
LIBNET_RAW4 / LIBNET_RAW4_ADV - 네트워크 계층. IPv4.
LIBNET_RAW6 / LIBNET_RAW6_ADV - 네트워크 계층. IPv6.
뒤의 ADV는 더 세밀한 조작을 하고 싶다면 붙이면 된다.
network interface : 랜카드 이름. 리눅스라면 eth0, ... 이고 맥OS라면 en0, ... 가 되겠다.
윈도우는? 몰라~
errbuf : 에러메시지를 담을 변수. 보통 char errbuf[LIBNET_ERRBUF_SIZE]; 로 선언하면 됨.
if (l == NULL)
{
fprintf(stderr, "%s", errbuf);
exit(EXIT_FAILURE);
}
물론, libnet_init이 실패했을 때 처리하는 코드가 이어진다.
else
실패하지 않았다면 다음을 진행해야겠지.
괄호( { / } ) 가 없으니 이 else가 영향을 미치는 문구는 다음 한 줄 뿐이라는 점을 주의.
/*
* Build the packet, remmebering that order IS important. We must
* build the packet from lowest protocol type on up as it would
* appear on the wire. So for our ARP packet:
*
* -------------------------------------------
* | Ethernet | ARP |
* -------------------------------------------
* ^ ^
* |------------------ |
* libnet_build_ethernet()--| |
* |
* libnet_build_arp()-----------|
*/
아니 이건 주석이고...
패킷을 만든다. (바이트) 순서가 중요하다는 점을 기억한다. 우리는 패킷을 가장 밑바닥 수준에서
만들 것이다. 그러므로 우리 ARP 패킷은:
(위 그림) 과 같은 모양새가 된다.
라는 뜻.
i = libnet_get_ipaddr4(l);
요게 else 다음에 오는.. 아 이렇게 말하니 영어공부하는 거 같다.
아무튼 i는 unsigned int(32bit) 형태다. libnet_get_ipaddr4는 libnet 객체에서 주소를 얻어오는 역할을 한다.
좀더 구체적으로 말하자면 libnet_t 객체가 가리키는 네트워크 인터페이스가 가진 ip 주소를 얻어오는 역할을 한다.
아님 말고.
t = libnet_autobuild_arp(
ARPOP_REPLY, /* operation type */
enet_src, /* sender hardware addr */
(u_int8_t *)&i, /* sender protocol addr */
enet_dst, /* target hardware addr */
(u_int8_t *)&i, /* target protocol addr */
l); /* libnet context */
arp 패킷을 만든다. 여기서 눈여겨 볼 것은 autobuild라는 함수명이다.
즉 auto가 아닌 것도 있다는 거지만, 귀찮기도 하고 잘 모르기도 하니까 넘어간다.
아무튼 이 함수를 쓰면 필요한 모든 값을 입력하지 않아도 arp 패킷을 만들 수 있다.
ARPOP_REPLY - 이름대로, ARP 패킷에 대한 응답 패킷이다.
ARPOP_REQUEST의 경우 요청 패킷이 된다.
enet_src - 이 변수는 libnet_test.h에 선언이 되어 있는데,
u_char enet_src[6] = {0x0d, 0x0e, 0x0a, 0x0d, 0x00, 0x00};
요렇다.
0d:0e:0a:0d:00:00 이라는 맥 어드레스를 의미하는데.. 왜 하필 이 숫자인지는 모르겠다.
아무튼 보내는 쪽의 맥 주소는 이것입니다 라는 의미이다.
u_char의 배열 형태를 하고 있다. (다음의 세 인자 모두 마찬가지.)
(u_int8_t *)&i;
복잡하다. C 코드에 대한 시시콜콜한 분석까지는 할 마음 없으니까 넘어간다.
아무튼 32비트 정수를 8비트 정수 4개가 연결된 배열로 취급하기 위해 집어넣은 약간의 꼼수다.
hardware addr는 하드웨어 주소 - 즉 MAC Address를 가리키는 것이고,
protocol addr는 프로토콜 주소 - 즉 IP주소(우리가 흔하게 보는)를 가리키는 것이다.
IP 주소는 논리적 주소이기 때문에 패킷이 도착하는 그날까지 바뀌지 않지만,
맥어드레스는 가는 곳마다 바뀌는 일이 부지기수다. 실제 목적지를 가리키는 것이 아니라
목적지까지 거쳐가야 할 중간 톨게이트를 가리키는 값이기 때문이다.
이하 두개는 위와 같으므로 생략. - ener_dst는 00:10:67:00:b1:86.
맨 마지막 l이 뭔지 모른다면 이명박 IQ가 틀림없으므로 이것도 생략.
if (t == -1)
{
fprintf(stderr, "Can't build ARP header: %s\n", libnet_geterror(l));
goto bad;
}
C세계의 거의 대부분이 그렇듯이, 만드는데 실패하면 뒷처리를 해야 한다.
t = libnet_autobuild_ethernet(
enet_dst, /* ethernet destination */
ETHERTYPE_ARP, /* protocol type */
l); /* libnet handle */
이더넷 헤더를 만든다. 이더넷 패킷이 아니라 이더넷 헤더라고 했다.
왜 헤더라고 표현했는지는 위로 올려보면 소스 내에 있는 주석에 그림으로 잘 표시되어 있다.
ETHERTYPE_ARP는 이 패킷이 ARP 패킷이라는 뜻이다.
ETHERTYPE_IP나 다른 것들도 가능하다.
if (t == -1)
{
fprintf(stderr, "Can't build ethernet header: %s\n",
libnet_geterror(l));
goto bad;
}
역시나 뒷처리 중.
if (libnet_adv_cull_packet(l, &packet, &packet_s) == -1)
{
fprintf(stderr, "%s", libnet_geterror(l));
}
libnet_adv_cull_packet. 이름만 들어 가지곤 도대체 뭔지 알 수가 없다.
libnet 문서는 짱나서 더 못봐주겠고...
외국 게시판을 뒤져보면 이 함수가 실질적으로 malloc을 호출한다고 한다.
인자들의 의미는 순서대로 libnet 객체, 페이로드(적하), 페이로드의 길이.
여기서 주의할 점은 페이로드를 우리가 미리 만들어서 인자로 넘겨주는 것이 아니라
그냥 빈 변수를 넘겨주면 자기가 내용을 알아서 채워준다는 것이다.
코드를 다시 보면 알겠지만 packet과 packet_s는 초기화조차 하지 않았다.
else
{
fprintf(stderr, "packet size: %d\n", packet_s);
libnet_adv_free_packet(l, packet);
}
계속 보자니 이것도 귀찮다.
c = libnet_write(l);
패킷을 쓴다. 여기서 쓴다 라고 하는 의미는 패킷을 만들어서 네트워크 카드에 연결된 선로로
흘려보낸다고 해석하면 된다. send packet.
if (c == -1)
{
fprintf(stderr, "Write error: %s\n", libnet_geterror(l));
goto bad;
}
몰라 귀찮아 넘어가
else
{
fprintf(stderr, "Wrote %d byte ARP packet from context \"%s\"; "
"check the wire.\n", c, libnet_cq_getlabel(l));
}
마지막이고 하니 성공한 작업이라도 역시 뒷처리를 해준다.
싫으면 말고.
libnet_destroy(l);
libnet 객체를 해제한다.
return (EXIT_SUCCESS);
bad:
libnet_destroy(l);
return (EXIT_FAILURE);
}
/* EOF */
마무리.
ps. 왜 나는 남들이 다 짜놓은 라이브러리도 자유자재로 활용 못해서 이딴 글이나 쓰고 있어야 되는 것인가.
ps2. 본문은 arp reply 패킷 보내는 거고... arp request 패킷 보내는 예제코드 따로 첨부함.
볼사람은 보고 말사람은 말고.
ps3. 해피캠퍼스 사무실에 지진이나 나라.
libpcap은 사용자도 많고 덩치도 크다보니 여기저기 자료가 많지만 libnet은 배포시에 딸려나오는
예제코드와 참고문서가 아니면 어디서 자료 구할데도 없지요.
에휴 기한도 지난 라우터 짠다고 내가 늙는다 늙어.
/*
* 아 몰라 너무 길어 귀찮아
*/
#if (HAVE_CONFIG_H)
#if ((_WIN32) && !(__CYGWIN__))
#include "../include/win32/config.h"
#else
#include "../include/config.h"
#endif
#endif
#include "./libnet_test.h"
딱히 설명할 필요는 없는 부분. 헤더파일도 뭐 그리 대단한 내용이 있는건 아니다.
int
main(int argc, char *argv[])
{
int c;
u_int32_t i;
libnet_t *l;
libnet_ptag_t t;
char *device = NULL;
u_int8_t *packet;
u_int32_t packet_s;
char errbuf[LIBNET_ERRBUF_SIZE];
printf("libnet 1.1 packet shaping: ARP[link -- autobuilding ethernet]\n");
if (argc > 1) // 명령 인자가 있다면
{
device = argv[1]; // 인자로 받은 장치를 쓰자.
}
l = libnet_init(
LIBNET_LINK_ADV, /* injection type */
device, /* network interface */
errbuf); /* errbuf */
libnet을 초기화한다.
injection type : 주입 모드? 직역하면 요상한 말이 돼버리는데, libnet의 처리를 어느 층에서 할 것인지를 결정한다.
LIBNET_LINK / LIBNET_LINK_ADV - 링크 계층
LIBNET_RAW4 / LIBNET_RAW4_ADV - 네트워크 계층. IPv4.
LIBNET_RAW6 / LIBNET_RAW6_ADV - 네트워크 계층. IPv6.
뒤의 ADV는 더 세밀한 조작을 하고 싶다면 붙이면 된다.
network interface : 랜카드 이름. 리눅스라면 eth0, ... 이고 맥OS라면 en0, ... 가 되겠다.
윈도우는? 몰라~
errbuf : 에러메시지를 담을 변수. 보통 char errbuf[LIBNET_ERRBUF_SIZE]; 로 선언하면 됨.
if (l == NULL)
{
fprintf(stderr, "%s", errbuf);
exit(EXIT_FAILURE);
}
물론, libnet_init이 실패했을 때 처리하는 코드가 이어진다.
else
실패하지 않았다면 다음을 진행해야겠지.
괄호( { / } ) 가 없으니 이 else가 영향을 미치는 문구는 다음 한 줄 뿐이라는 점을 주의.
/*
* Build the packet, remmebering that order IS important. We must
* build the packet from lowest protocol type on up as it would
* appear on the wire. So for our ARP packet:
*
* -------------------------------------------
* | Ethernet | ARP |
* -------------------------------------------
* ^ ^
* |------------------ |
* libnet_build_ethernet()--| |
* |
* libnet_build_arp()-----------|
*/
아니 이건 주석이고...
패킷을 만든다. (바이트) 순서가 중요하다는 점을 기억한다. 우리는 패킷을 가장 밑바닥 수준에서
만들 것이다. 그러므로 우리 ARP 패킷은:
(위 그림) 과 같은 모양새가 된다.
라는 뜻.
i = libnet_get_ipaddr4(l);
요게 else 다음에 오는.. 아 이렇게 말하니 영어공부하는 거 같다.
아무튼 i는 unsigned int(32bit) 형태다. libnet_get_ipaddr4는 libnet 객체에서 주소를 얻어오는 역할을 한다.
좀더 구체적으로 말하자면 libnet_t 객체가 가리키는 네트워크 인터페이스가 가진 ip 주소를 얻어오는 역할을 한다.
아님 말고.
t = libnet_autobuild_arp(
ARPOP_REPLY, /* operation type */
enet_src, /* sender hardware addr */
(u_int8_t *)&i, /* sender protocol addr */
enet_dst, /* target hardware addr */
(u_int8_t *)&i, /* target protocol addr */
l); /* libnet context */
arp 패킷을 만든다. 여기서 눈여겨 볼 것은 autobuild라는 함수명이다.
즉 auto가 아닌 것도 있다는 거지만, 귀찮기도 하고 잘 모르기도 하니까 넘어간다.
아무튼 이 함수를 쓰면 필요한 모든 값을 입력하지 않아도 arp 패킷을 만들 수 있다.
ARPOP_REPLY - 이름대로, ARP 패킷에 대한 응답 패킷이다.
ARPOP_REQUEST의 경우 요청 패킷이 된다.
enet_src - 이 변수는 libnet_test.h에 선언이 되어 있는데,
u_char enet_src[6] = {0x0d, 0x0e, 0x0a, 0x0d, 0x00, 0x00};
요렇다.
0d:0e:0a:0d:00:00 이라는 맥 어드레스를 의미하는데.. 왜 하필 이 숫자인지는 모르겠다.
아무튼 보내는 쪽의 맥 주소는 이것입니다 라는 의미이다.
u_char의 배열 형태를 하고 있다. (다음의 세 인자 모두 마찬가지.)
(u_int8_t *)&i;
복잡하다. C 코드에 대한 시시콜콜한 분석까지는 할 마음 없으니까 넘어간다.
아무튼 32비트 정수를 8비트 정수 4개가 연결된 배열로 취급하기 위해 집어넣은 약간의 꼼수다.
hardware addr는 하드웨어 주소 - 즉 MAC Address를 가리키는 것이고,
protocol addr는 프로토콜 주소 - 즉 IP주소(우리가 흔하게 보는)를 가리키는 것이다.
IP 주소는 논리적 주소이기 때문에 패킷이 도착하는 그날까지 바뀌지 않지만,
맥어드레스는 가는 곳마다 바뀌는 일이 부지기수다. 실제 목적지를 가리키는 것이 아니라
목적지까지 거쳐가야 할 중간 톨게이트를 가리키는 값이기 때문이다.
이하 두개는 위와 같으므로 생략. - ener_dst는 00:10:67:00:b1:86.
맨 마지막 l이 뭔지 모른다면 이명박 IQ가 틀림없으므로 이것도 생략.
if (t == -1)
{
fprintf(stderr, "Can't build ARP header: %s\n", libnet_geterror(l));
goto bad;
}
C세계의 거의 대부분이 그렇듯이, 만드는데 실패하면 뒷처리를 해야 한다.
t = libnet_autobuild_ethernet(
enet_dst, /* ethernet destination */
ETHERTYPE_ARP, /* protocol type */
l); /* libnet handle */
이더넷 헤더를 만든다. 이더넷 패킷이 아니라 이더넷 헤더라고 했다.
왜 헤더라고 표현했는지는 위로 올려보면 소스 내에 있는 주석에 그림으로 잘 표시되어 있다.
ETHERTYPE_ARP는 이 패킷이 ARP 패킷이라는 뜻이다.
ETHERTYPE_IP나 다른 것들도 가능하다.
if (t == -1)
{
fprintf(stderr, "Can't build ethernet header: %s\n",
libnet_geterror(l));
goto bad;
}
역시나 뒷처리 중.
if (libnet_adv_cull_packet(l, &packet, &packet_s) == -1)
{
fprintf(stderr, "%s", libnet_geterror(l));
}
libnet_adv_cull_packet. 이름만 들어 가지곤 도대체 뭔지 알 수가 없다.
libnet 문서는 짱나서 더 못봐주겠고...
외국 게시판을 뒤져보면 이 함수가 실질적으로 malloc을 호출한다고 한다.
인자들의 의미는 순서대로 libnet 객체, 페이로드(적하), 페이로드의 길이.
여기서 주의할 점은 페이로드를 우리가 미리 만들어서 인자로 넘겨주는 것이 아니라
그냥 빈 변수를 넘겨주면 자기가 내용을 알아서 채워준다는 것이다.
코드를 다시 보면 알겠지만 packet과 packet_s는 초기화조차 하지 않았다.
else
{
fprintf(stderr, "packet size: %d\n", packet_s);
libnet_adv_free_packet(l, packet);
}
계속 보자니 이것도 귀찮다.
c = libnet_write(l);
패킷을 쓴다. 여기서 쓴다 라고 하는 의미는 패킷을 만들어서 네트워크 카드에 연결된 선로로
흘려보낸다고 해석하면 된다. send packet.
if (c == -1)
{
fprintf(stderr, "Write error: %s\n", libnet_geterror(l));
goto bad;
}
몰라 귀찮아 넘어가
else
{
fprintf(stderr, "Wrote %d byte ARP packet from context \"%s\"; "
"check the wire.\n", c, libnet_cq_getlabel(l));
}
마지막이고 하니 성공한 작업이라도 역시 뒷처리를 해준다.
싫으면 말고.
libnet_destroy(l);
libnet 객체를 해제한다.
return (EXIT_SUCCESS);
bad:
libnet_destroy(l);
return (EXIT_FAILURE);
}
/* EOF */
마무리.
ps. 왜 나는 남들이 다 짜놓은 라이브러리도 자유자재로 활용 못해서 이딴 글이나 쓰고 있어야 되는 것인가.
ps2. 본문은 arp reply 패킷 보내는 거고... arp request 패킷 보내는 예제코드 따로 첨부함.
볼사람은 보고 말사람은 말고.
ps3. 해피캠퍼스 사무실에 지진이나 나라.

댓글을 달아 주세요
내용관 상관없는 댓글,
(방명록에 쓰면 안볼 것 같아서)
니가 준 dvd가 화면은 재생이 되는데 음악이 안들려.
음악은 링크로 트는건가벼.
내용과 상관없는 답글.
그거 인코딩한 다음에 넣은거라 상관없을텐데
그리고 어차피 음악따위 나오나마나..
압축파일이나 다시 확인해보시오
도움이 많이 되었습니다. 감사합니다~
저기... 지나가는 초보가 글 남겨서 죄송합니다.
네트워크 내부의 살아있는 Host를 알기 위해서
Arp 브로드 캐스트로 패킷 날려보고 응답오는 Host에 대해서만 포트스캔을 하는 프로그램을 개발중인데요
제가 libnet쪽에 문외한이라서 그러는데
이런 바보같은 질문 드려서 죄송한데
gcc로 컴파일 할때 옵션좀 알려주시면 안될까요?
arp request 패킷 보내는 예제코드 이 소스 그대로 컴파일하면 같은 네트워크상의 모든 pc에게 arp패킷을 쏘는건가여??
헐 이런 누추한 곳에 리플이..
너무 늦어서 지금 답글 달아봐야 의미가 없겠군요
http://kldp.org/node/108763 이글 쓰신분이라면 더더욱.. ㅡㅡ;
누가 여기에 댓글을 달거란 생각을 못해서.. 확인을 못해 죄송합니다 ㅡㅡ;
kldp에다가 제가 쓴 글이 맞네요 ㅎㅎ
libnet 완벽하게 이해하지는 못했지만
일단 결과값 받는것은 확인했고
다음 단계 하고 있어요^^
유용한 자료 감사합니다^^