패킷인사이드 주인장 입니다. 12월쯤에 제가 블로그를 처음 시작했는데, 미쳐 잊고 있다 찾아보니 12월10일이 오픈일 이었네요. 어느덧 벌써 4년이라는 시간이 흘렀습니다. 2013년은 여느해 보다 개인적으로는 바쁘기도 하면서 어딘가 한 구석이 비워있는 듯한 느낌이었던것 같습니다. 2014년은 저도 다시 재 정비를 하고 더욱 많은 내용을 여러분들과 공유할 수 있도록 하겠습니다. 아직도 소개해 드리고 싶은 내용이 너무나도 많거든요.
이제 얼마 남지 않은 2013년 마무리 잘 하시고요, 2014년에 뵙겠습니다.
새해 복 많이 받으세요 그리고 모두 행복하세요 ^^
/Rigel
2013년 12월 27일 금요일
2013년 11월 13일 수요일
CaseStudy, FTP 접속 지연 원인 - 10초의 흔적을 찾아라!
오래간만에 쓰는 포스팅인것 같습니다. 이런저런 일들이 많다보니 블로깅도 게을러지고 있네요. :-)
오늘은 CaseStudy 형태로 애플리케이션의 접속 지연에 대해서 실제 사례를 한번 다뤄볼까 합니다. FTP 접속을 언급하려고 하는데, 이전에도 한번 사례로 FTP 를 다룬적이 있습니다. 약간 비슷할 수도 있지만 관점이 조금 다르긴 합니다. 일단 분석하게 된 이유는 이렇습니다.
FTP 를 통해서 특정 시스템에 접속하는데 접속이 바로 이뤄지지 않고 지연이 발생하는 것입니다.
C:\>ftp 192.168.0.1
Connected to 192.168.0.1.
여기서 10 초정도의 지연이 발생하고 다음과 같이 접속이 이뤄집니다.
C:\>ftp 192.168.0.1
Connected to 192.168.0.1.
220 TEST FTP server (Version 6.4/OpenBSD/Linux-ftpd-0.17) ready.
User (192.168.0.1:(none)):
일단 시스템에서 패킷 덤프를 해 보았습니다. 일반적인 3way Handshake 과정이 이뤄지고 있습니다. 클라이언트인 192.168.98.128 이 comm-gw(192.168.0.1) FTP 포트에 SYN 패킷을 보내면서 연결 시도를 합니다.
11:15:31.363275 IP 192.168.98.128.1087 > comm-gw.ftp: S 1725038219:1725038219(0) win 65535 <mss 1460,nop,nop,sackOK>
11:15:31.363305 IP comm-gw.ftp > 192.168.98.128.1087: S 3794060333:3794060333(0) ack 1725038220 win 5840 <mss 1460,nop,nop,sackOK>
11:15:31.363554 IP 192.168.98.128.1087 > comm-gw.ftp: . ack 1 win 65535
11:15:37.309023 IP 192.168.98.128.netbios-dgm > 192.168.255.255.netbios-dgm: NBT UDP PACKET(138)
11:15:42.577429 IP comm-gw.ftp > 192.168.98.128.1087: P 1:72(71) ack 1 win 5840
11:15:42.714480 IP 192.168.98.128.1087 > comm-gw.ftp: . ack 72 win 65464
그런데 ACK 까지 전달이 되고 통신을 하는 과정에서 지연이 보입니다. 11:15:31초 후에 42초 쯤 관련된 트래픽이 나타납니다. 클라이언트 관점에서 봤을때는 연결이 이뤄지고 서버에서 응답이 오기까지 지연이 보이는 것으로 추정됩니다.
일반적으로 이런 지연이 발생되는 원인중에 하나로 Name Lookup 이 있습니다. 그래서 /etc/hosts 파일에 해당 클라이언트 IP 를 넣어주고 패킷 덤프를 다시 해 봅니다.
# tcpdump -i eth4 host 192.168.98.128
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth4, link-type EN10MB (Ethernet), capture size 96 bytes
11:18:11.836148 IP aa-05-01.proofd > comm-gw.ftp: S 3515309442:3515309442(0) win 65535 <mss 1460,nop,nop,sackOK>
11:18:11.836173 IP comm-gw.ftp > aa-05-01.proofd: S 2006125397:2006125397(0) ack 3515309443 win 5840 <mss 1460,nop,nop,sackOK>
11:18:11.836340 IP aa-05-01.proofd > comm-gw.ftp: . ack 1 win 65535
11:18:11.837724 IP comm-gw.ftp > aa-05-01.proofd: P 1:72(71) ack 1 win 5840
11:18:11.955346 IP aa-05-01.proofd > comm-gw.ftp: . ack 72 win 65464
^C
그런데, 앞서 본 것과 달리 속도 지연이 없어졌습니다. 11:18:11 안에 모든 과정들이 끝나버렸습니다. 10초 이상 지연이 발생한 것과는 대조적입니다. 조금 더 자세히 살펴보기 위해 ftp 데몬을 추적해 보기로 했습니다. 다만, ftpd 데몬이 지속적으로 떠 있는 것이 아니라 inetd 데몬에 의해서 접속시 마다 프로세스가 생성되므로 다음과 같이 해 보았습니다.
1초마다 프로세스를 살펴봐서 in.ftp 문자열이 검색되면 해당 프로세스 ID 를 넘겨받아 strace -p PID 로 하는 것입니다.
# while true; do ps -ef | grep in.ftp | grep -v grep | gawk -F' ' '{system("strace -p "$2)}'; sleep 1; done
Process 16864 attached - interrupt to quit
restart_syscall(<... resuming interrupted call ...>) = 1
ioctl(4, FIONREAD, [43]) = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, [16]) = 43
close(4) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 28) = 0
fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065750, 265867}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 3000) = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [43]) = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [16]) = 43
close(4) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, 28) = 0
fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065752, 399158}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 6000) = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [43]) = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, [16]) = 43
close(4) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, 28) = 0
fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065753, 199402}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 5000) = 0 (Timeout)
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 28) = 0
fcntl64(5, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(5, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065758, 201686}, NULL) = 0
poll([{fd=5, events=POLLOUT}], 1, 0) = 1 ([{fd=5, revents=POLLOUT}])
send(5, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=5, events=POLLIN}], 1, 3000) = 1 ([{fd=5, revents=POLLIN}])
ioctl(5, FIONREAD, [43]) = 0
recvfrom(5, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [16]) = 43
close(4) = 0
close(5) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, 28) = 0
fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065758, 888660}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 6000) = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [43]) = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, [16]) = 43
close(4) = 0
open("/etc/nologin", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/etc/ftpwelcome", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
uname({sys="Linux", node="TEST", ...}) = 0
stat64("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=119, ...}) = 0
stat64("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=119, ...}) = 0
open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=336, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f4f000
read(4, "127.0.0.1\tlocalhost\n127.0.1.1\tTES"..., 4096) = 336
read(4, ""..., 4096) = 0
close(4) = 0
munmap(0xb7f4f000, 4096) = 0
fstat64(1, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f4f000
write(1, "220 TEST FTP server (Version"..., 71) = 71
rt_sigaction(SIGALRM, {0x804ef40, [ALRM], SA_RESTART}, {SIG_DFL}, 8) = 0
alarm(900) = 0
fstat64(0, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f4e000
read(0, ^C <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0, ^C <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0, quit
^\ <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0, ^C <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0,
^C <unfinished ...>
Process 16864 detached
^\Quit
Process 16864 attached - interrupt to quit
read(0, ^Z
여기서는 실제로 보여주는 것이 한계가 있지만, strace 로 해서 보면 name lookup 하는 부분에서 딜레이가 발생하는 것이 보입니다. 즉, 이런 형태를 검증해 보는 하나의 방법으로 보시면 될것 같습니다. 하지만, 여기에 문제가 있습니다. 접속하는 클라이언트가 수백대가 넘게 있습니다. 그 IP 들을 일일이 넣어주기도 쉽지가 않습니다. 분명히 또 다른 방법이 있을것입니다. 'man in.ftpd' 를 통해서 세부적인 옵션을 확인해 보았더니 -n 이라는 것이 있습니다.
-n Use numeric IP addresses in logs instead of doing hostname lookup.
ftpd 데몬을 많이 써보았지만 굳이 옵션을 찾아볼 일이 없었는데 -n 이 호스트이름 대신 IP 주소로 사용하여 로그를 기록하게 되어 있네요. 자 그래서 -n 옵션을 사용해 반영했더니, 동일하게 모든 클라이언트에서 접속해도 빠른 속도로 접속이 이뤄집니다.
/etc/hosts 파일에 개별적으로 사용할 수도 있지만 좀더 광범위하고 효율적인 작업면에서는 -n 옵션이 더욱 적절했던 것입니다.
패킷덤프를 통해서 패킷의 흐름을 살펴보고, 서버에서의 응답에 지연이 있는 것으로 판단되어 서버단에서 문제를 찾아들어가며 해결한 경우입니다. 문제가 발생할 경우 해결하기 위한 방법에는 메뉴얼 처럼 딱 정해진것이 없습니다. 기본 해결 방법은 같을 수 있지만 그것을 찾아들어가기 위한 것은 다양합니다. 이것또한 그 중에 하나입니다.
어떤이는 접속이 되니까 그냥 그대로 사용할 수도 있고 또 다른 사람은 왜 지연이 있을까 하고 의문을 갖게 됩니다. 물론 이런 지연이 영향을 미칠만큼이냐 또는 아니냐에 따라서 판단은 달라지게 됩니다. 하지만 이런것에 Question 을 가지고 계속 질문해 보면 그 답을 찾는 과정에서 많은 것을 얻고 배우게 됩니다. 바로 이런것이 Experience 입니다. 저 또한 블로그를 하는 이유가 이러한 경험을 여러분들과 함께 공유하기 위함입니다.
자, 그럼 오늘은 여기까지 하고 마무리 짓겠습니다.
문제가 발생했을때 실마리를 어떻게 풀어나갈것인지 참고가 되었으면 좋겠습니다.
/Rigel
오늘은 CaseStudy 형태로 애플리케이션의 접속 지연에 대해서 실제 사례를 한번 다뤄볼까 합니다. FTP 접속을 언급하려고 하는데, 이전에도 한번 사례로 FTP 를 다룬적이 있습니다. 약간 비슷할 수도 있지만 관점이 조금 다르긴 합니다. 일단 분석하게 된 이유는 이렇습니다.
FTP 를 통해서 특정 시스템에 접속하는데 접속이 바로 이뤄지지 않고 지연이 발생하는 것입니다.
C:\>ftp 192.168.0.1
Connected to 192.168.0.1.
여기서 10 초정도의 지연이 발생하고 다음과 같이 접속이 이뤄집니다.
C:\>ftp 192.168.0.1
Connected to 192.168.0.1.
220 TEST FTP server (Version 6.4/OpenBSD/Linux-ftpd-0.17) ready.
User (192.168.0.1:(none)):
일단 시스템에서 패킷 덤프를 해 보았습니다. 일반적인 3way Handshake 과정이 이뤄지고 있습니다. 클라이언트인 192.168.98.128 이 comm-gw(192.168.0.1) FTP 포트에 SYN 패킷을 보내면서 연결 시도를 합니다.
11:15:31.363275 IP 192.168.98.128.1087 > comm-gw.ftp: S 1725038219:1725038219(0) win 65535 <mss 1460,nop,nop,sackOK>
11:15:31.363305 IP comm-gw.ftp > 192.168.98.128.1087: S 3794060333:3794060333(0) ack 1725038220 win 5840 <mss 1460,nop,nop,sackOK>
11:15:31.363554 IP 192.168.98.128.1087 > comm-gw.ftp: . ack 1 win 65535
11:15:37.309023 IP 192.168.98.128.netbios-dgm > 192.168.255.255.netbios-dgm: NBT UDP PACKET(138)
11:15:42.577429 IP comm-gw.ftp > 192.168.98.128.1087: P 1:72(71) ack 1 win 5840
11:15:42.714480 IP 192.168.98.128.1087 > comm-gw.ftp: . ack 72 win 65464
그런데 ACK 까지 전달이 되고 통신을 하는 과정에서 지연이 보입니다. 11:15:31초 후에 42초 쯤 관련된 트래픽이 나타납니다. 클라이언트 관점에서 봤을때는 연결이 이뤄지고 서버에서 응답이 오기까지 지연이 보이는 것으로 추정됩니다.
일반적으로 이런 지연이 발생되는 원인중에 하나로 Name Lookup 이 있습니다. 그래서 /etc/hosts 파일에 해당 클라이언트 IP 를 넣어주고 패킷 덤프를 다시 해 봅니다.
# tcpdump -i eth4 host 192.168.98.128
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth4, link-type EN10MB (Ethernet), capture size 96 bytes
11:18:11.836148 IP aa-05-01.proofd > comm-gw.ftp: S 3515309442:3515309442(0) win 65535 <mss 1460,nop,nop,sackOK>
11:18:11.836173 IP comm-gw.ftp > aa-05-01.proofd: S 2006125397:2006125397(0) ack 3515309443 win 5840 <mss 1460,nop,nop,sackOK>
11:18:11.836340 IP aa-05-01.proofd > comm-gw.ftp: . ack 1 win 65535
11:18:11.837724 IP comm-gw.ftp > aa-05-01.proofd: P 1:72(71) ack 1 win 5840
11:18:11.955346 IP aa-05-01.proofd > comm-gw.ftp: . ack 72 win 65464
^C
그런데, 앞서 본 것과 달리 속도 지연이 없어졌습니다. 11:18:11 안에 모든 과정들이 끝나버렸습니다. 10초 이상 지연이 발생한 것과는 대조적입니다. 조금 더 자세히 살펴보기 위해 ftp 데몬을 추적해 보기로 했습니다. 다만, ftpd 데몬이 지속적으로 떠 있는 것이 아니라 inetd 데몬에 의해서 접속시 마다 프로세스가 생성되므로 다음과 같이 해 보았습니다.
1초마다 프로세스를 살펴봐서 in.ftp 문자열이 검색되면 해당 프로세스 ID 를 넘겨받아 strace -p PID 로 하는 것입니다.
# while true; do ps -ef | grep in.ftp | grep -v grep | gawk -F' ' '{system("strace -p "$2)}'; sleep 1; done
Process 16864 attached - interrupt to quit
restart_syscall(<... resuming interrupted call ...>) = 1
ioctl(4, FIONREAD, [43]) = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, [16]) = 43
close(4) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 28) = 0
fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065750, 265867}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 3000) = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [43]) = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [16]) = 43
close(4) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, 28) = 0
fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065752, 399158}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 6000) = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [43]) = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, [16]) = 43
close(4) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, 28) = 0
fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065753, 199402}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 5000) = 0 (Timeout)
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 5
connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, 28) = 0
fcntl64(5, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(5, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065758, 201686}, NULL) = 0
poll([{fd=5, events=POLLOUT}], 1, 0) = 1 ([{fd=5, revents=POLLOUT}])
send(5, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=5, events=POLLIN}], 1, 3000) = 1 ([{fd=5, revents=POLLIN}])
ioctl(5, FIONREAD, [43]) = 0
recvfrom(5, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("8.8.8.8")}, [16]) = 43
close(4) = 0
close(5) = 0
socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, 28) = 0
fcntl64(4, F_GETFL) = 0x2 (flags O_RDWR)
fcntl64(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1382065758, 888660}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\360\275\1\0\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 43, MSG_NOSIGNAL) = 43
poll([{fd=4, events=POLLIN}], 1, 6000) = 1 ([{fd=4, revents=POLLIN}])
ioctl(4, FIONREAD, [43]) = 0
recvfrom(4, "\360\275\201\202\0\1\0\0\0\0\0\0\003128\00298\0015\003111\7in-addr"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("168.126.63.1")}, [16]) = 43
close(4) = 0
open("/etc/nologin", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
open("/etc/ftpwelcome", O_RDONLY|O_LARGEFILE) = -1 ENOENT (No such file or directory)
uname({sys="Linux", node="TEST", ...}) = 0
stat64("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=119, ...}) = 0
stat64("/etc/resolv.conf", {st_mode=S_IFREG|0644, st_size=119, ...}) = 0
open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=336, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f4f000
read(4, "127.0.0.1\tlocalhost\n127.0.1.1\tTES"..., 4096) = 336
read(4, ""..., 4096) = 0
close(4) = 0
munmap(0xb7f4f000, 4096) = 0
fstat64(1, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f4f000
write(1, "220 TEST FTP server (Version"..., 71) = 71
rt_sigaction(SIGALRM, {0x804ef40, [ALRM], SA_RESTART}, {SIG_DFL}, 8) = 0
alarm(900) = 0
fstat64(0, {st_mode=S_IFSOCK|0777, st_size=0, ...}) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f4e000
read(0, ^C <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0, ^C <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0, quit
^\ <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0, ^C <unfinished ...>
Process 16864 detached
Process 16864 attached - interrupt to quit
read(0,
^C <unfinished ...>
Process 16864 detached
^\Quit
Process 16864 attached - interrupt to quit
read(0, ^Z
여기서는 실제로 보여주는 것이 한계가 있지만, strace 로 해서 보면 name lookup 하는 부분에서 딜레이가 발생하는 것이 보입니다. 즉, 이런 형태를 검증해 보는 하나의 방법으로 보시면 될것 같습니다. 하지만, 여기에 문제가 있습니다. 접속하는 클라이언트가 수백대가 넘게 있습니다. 그 IP 들을 일일이 넣어주기도 쉽지가 않습니다. 분명히 또 다른 방법이 있을것입니다. 'man in.ftpd' 를 통해서 세부적인 옵션을 확인해 보았더니 -n 이라는 것이 있습니다.
-n Use numeric IP addresses in logs instead of doing hostname lookup.
ftpd 데몬을 많이 써보았지만 굳이 옵션을 찾아볼 일이 없었는데 -n 이 호스트이름 대신 IP 주소로 사용하여 로그를 기록하게 되어 있네요. 자 그래서 -n 옵션을 사용해 반영했더니, 동일하게 모든 클라이언트에서 접속해도 빠른 속도로 접속이 이뤄집니다.
/etc/hosts 파일에 개별적으로 사용할 수도 있지만 좀더 광범위하고 효율적인 작업면에서는 -n 옵션이 더욱 적절했던 것입니다.
패킷덤프를 통해서 패킷의 흐름을 살펴보고, 서버에서의 응답에 지연이 있는 것으로 판단되어 서버단에서 문제를 찾아들어가며 해결한 경우입니다. 문제가 발생할 경우 해결하기 위한 방법에는 메뉴얼 처럼 딱 정해진것이 없습니다. 기본 해결 방법은 같을 수 있지만 그것을 찾아들어가기 위한 것은 다양합니다. 이것또한 그 중에 하나입니다.
어떤이는 접속이 되니까 그냥 그대로 사용할 수도 있고 또 다른 사람은 왜 지연이 있을까 하고 의문을 갖게 됩니다. 물론 이런 지연이 영향을 미칠만큼이냐 또는 아니냐에 따라서 판단은 달라지게 됩니다. 하지만 이런것에 Question 을 가지고 계속 질문해 보면 그 답을 찾는 과정에서 많은 것을 얻고 배우게 됩니다. 바로 이런것이 Experience 입니다. 저 또한 블로그를 하는 이유가 이러한 경험을 여러분들과 함께 공유하기 위함입니다.
자, 그럼 오늘은 여기까지 하고 마무리 짓겠습니다.
문제가 발생했을때 실마리를 어떻게 풀어나갈것인지 참고가 되었으면 좋겠습니다.
/Rigel
2013년 10월 23일 수요일
구글에서 제공하는 무료 DDoS 방어 서비스, 프로젝트 쉴드(Project Shield)
구글에서 최근 프로젝트 쉴드라는 것을 발표했습니다. 구글의 인프라스트럭처를 이용해 분산서비스거부 공격(DDoS)을 막을 수 있는 서비스 입니다. 서비스 받고 있는 호스팅을 이동한다든지의 작업 없이 구글의 서비스를 통해 DDoS 를 완하시키겠다는 것입니다.
현재 이 서비스는 무료로 이용할 수 있으나, 초대된 웹 사이트만 이용이 가능합니다. 지원을 하게 되면 구글이 말하는 'Trusted Tester' 로 선정되고 사용하게 됩니다. 가입을 하고 선정되면 설정할 수 있는 정보를 이메일을 통해 전달받는것 같습니다.
아마도 이 서비스는 DNS 설정을 통해 구글쪽으로 변경하고 거기서 구글의 인프라를 통해 DDoS 와 같은 이벤트가 급격하게 증가시 보호 역할을 하게 되는것으로 보입니다. 다른 외부에서 제공하는 DDoS 차단 서비스를 유사하게 무료로 제공하는 것으로 중.소기업의 웹 사이트들이 이용하기에 괜챦아 보입니다. DDoS 공격을 받고 있는 사이트가 있다면 지금 한번 신청해 보세요.
[참고]
1. 프로젝트 쉴드
http://projectshield.withgoogle.com/
2. 구글 아이디어 프로젝트
http://www.google.com/ideas/projects/
현재 이 서비스는 무료로 이용할 수 있으나, 초대된 웹 사이트만 이용이 가능합니다. 지원을 하게 되면 구글이 말하는 'Trusted Tester' 로 선정되고 사용하게 됩니다. 가입을 하고 선정되면 설정할 수 있는 정보를 이메일을 통해 전달받는것 같습니다.
아마도 이 서비스는 DNS 설정을 통해 구글쪽으로 변경하고 거기서 구글의 인프라를 통해 DDoS 와 같은 이벤트가 급격하게 증가시 보호 역할을 하게 되는것으로 보입니다. 다른 외부에서 제공하는 DDoS 차단 서비스를 유사하게 무료로 제공하는 것으로 중.소기업의 웹 사이트들이 이용하기에 괜챦아 보입니다. DDoS 공격을 받고 있는 사이트가 있다면 지금 한번 신청해 보세요.
[참고]
1. 프로젝트 쉴드
http://projectshield.withgoogle.com/
2. 구글 아이디어 프로젝트
http://www.google.com/ideas/projects/
2013년 10월 16일 수요일
SQL 쿼리로 패킷 데이터를 조회한다고 ?
패킷데이터를 SQL 쿼리문 형태로 출력할 수 있는 PacketQ 에 대해서 소개해 볼까합니다. 패킷데이터를 간단하고 빠르게 출력해 낼 수 있는 도구입니다. 원래 이 도구가 DNS2db 라는 이름으로 사용되다가 2011년에 이름을 PacketQ 로 변경했습니다. 변경한 이유는 원래의 이름에서 알 수 있듯이 주로 DNS 데이터 중심으로 데이터를 뽑아냈었는데, 다른 프로토콜 까지 확장해 SQL 쿼리문으로 뽑아내고자 하였기 때문입니다. 하지만 아직까지는 자유롭게 처리할 수 있는 것이 DNS 데이터쪽으로 맞춰져 있습니다. 그러므로 DNS 에 한해 쉽게 데이터를 뽑아내고자 하시는 분들한테는 유용할 수 있습니다. DNS 관리자 분들에게는 더욱 유용할수도 있겠습니다.
일단 다운로드 및 세부 정보는 다음 경로에서 확인할 수 있습니다:
https://github.com/dotse/packetq
주요한 기능을 보면 PCAP 파일을 빠르게 파싱할 수 있다는 것을 강점으로 내세우고 있고요 SQL 쿼리문 형태로 사용이 가능합니다. SQL 에 익숙하신 분에게는 더없이 좋겠죠. XML, CSV, JSON, Table 형태로도 데이터를 출력시킬 수 있습니다. 아직은 DNS 와 ICMP 프로토콜 정도만 지원하고 있다는 점입니다. 그리고 PacketQ 가 SQL 쿼리문 형태로 사용할 수 있다고 하지만 완벽한 SQL 문 형태로 지원하는 것은 아닙니다. 조인, 서브쿼리, distinct, like 같은것은 사용할 수 없습니다. 사용가능한 문법은 다음 페이지를 참고해 보시면 됩니다.
https://github.com/dotse/PacketQ/blob/master/src/grammar
간단한 사용방법을 몇개 살펴보겠습니다. SQL 쿼리 같이 * 로 해서 전체 데이터를 다 출력할 수도 있지만 특정필드로도 제한할 수 있습니다. 일단, DNS 데이터에서 추출해 낼 수 있는 데이터 형태는 다음과 같습니다.
{ "name": "id","type": "int" },
{ "name": "s","type": "int" },
{ "name": "us","type": "int" },
{ "name": "ether_type","type": "int" },
{ "name": "src_port","type": "int" },
{ "name": "dst_port","type": "int" },
{ "name": "src_addr","type": "text" },
{ "name": "dst_addr","type": "text" },
{ "name": "protocol","type": "int" },
{ "name": "ip_ttl","type": "int" },
{ "name": "fragments","type": "int" },
{ "name": "qname","type": "text" },
{ "name": "aname","type": "text" },
{ "name": "msg_id","type": "int" },
{ "name": "msg_size","type": "int" },
{ "name": "opcode","type": "int" },
{ "name": "rcode","type": "int" },
{ "name": "extended_rcode","type": "int" },
{ "name": "edns_version","type": "int" },
{ "name": "z","type": "int" },
{ "name": "udp_size","type": "int" },
{ "name": "qd_count","type": "int" },
{ "name": "an_count","type": "int" },
{ "name": "ns_count","type": "int" },
{ "name": "ar_count","type": "int" },
{ "name": "qtype","type": "int" },
{ "name": "qclass","type": "int" },
{ "name": "atype","type": "int" },
{ "name": "aclass","type": "int" },
{ "name": "attl","type": "int" },
{ "name": "aa","type": "bool" },
{ "name": "tc","type": "bool" },
{ "name": "rd","type": "bool" },
{ "name": "cd","type": "bool" },
{ "name": "ra","type": "bool" },
{ "name": "ad","type": "bool" },
{ "name": "do","type": "bool" },
{ "name": "edns0","type": "bool" },
{ "name": "qr","type": "bool" }
이중에서 qtype 과 opcode 를 출력하고 총 3개의 데이터로 제한을 한다면 다음과 같이 쿼리가 만들어질 수 있습니다. -s 뒤에 사용할 쿼리를 넣어주면 됩니다.
# ./packetq -s "select qname,opcode from dns limit 3" test.pcap
[
{
"table_name": "result-0",
"query": "select qname,opcode from dns limit 3",
"head": [
{ "name": "qname","type": "text" },
{ "name": "opcode","type": "int" }
],
"data": [
["n.search.naver.com.",0],
["n.search.naver.com.",0],
["n.search.naver.com.",0]
]
}
]
어떤 출발지에서 가장 많은 DNS 쿼리 요청을 했는지 출력해 봅니다.
# ./packetq -s "select src_addr,count(*) as count from dns group by src_addr order by count desc limit 1" sample.pcap
[
size = 2
{
"table_name": "result-0",
"query": "select src_addr,count(*) as count from dns group by src_addr order by count desc limit 1",
"head": [
{ "name": "src_addr","type": "text" },
{ "name": "count","type": "int" }
],
"data": [
["172.xx.xx.52",4]
]
}
]
만약 가장 많은 쿼리가 요청된 것을 뽑아낸다면 다음과 같이 합니다. qname 으로 groupby 를 해 줍니다. SQL 에 익숙하신 분들이라면 쿼리만 보고서도 쉽게 이해가 되실 것입니다. 하지만 SQL 경험이 없으신 분들은 group by 가 무엇인지도 혼란스러울 수도 있습니다. 여기서는 SQL 쿼리문 까지는 다루기 힘들기 때문에 의미만을 설명드리겠습니다. qname 으로 그룹을 만들어 주고 각 qname 마다의 카운트를 생성합니다. 그리고 정렬을 count 로 해서 가장 많은 숫자가 처음에 나오게 하고 limit 를 통해 출력데이터를 1개로만 합니다. 그 결과가 아래와 같습니다 :
# ./packetq -s "select qname,count(*) as count from dns group by qname order by count desc limit 1 " test.pcap
[
size = 1513
{
"table_name": "result-0",
"query": "select qname,count(*) as count from dns group by qname order by count desc limit 1 ",
"head": [
{ "name": "qname","type": "text" },
{ "name": "count","type": "int" }
],
"data": [
["resolver3.xxxx[삭제].com.",238]
]
}
]
이번에는 쿼리타입을 출력해 볼까요? 쿼리타입이 기본적으로는 정수 값입니다. 그런데 정수값으로 보면 사람한테는 익숙하지 않습니다. 이때 NAME 펑션 기능을 이용해 쿼리타입(qtype)을 텍스트 형태로 출력시킬 수 있습니다. qtype 으로 그룹을 묶고 타입과, 카운트 정보를 뽑아서 가장 많은 순서대로 출력을 시킵니다.
# ./packetq -s "SELECT NAME( 'qtype' , qtype ) AS qt, COUNT(*) AS antal FROM dns GROUP BY qtype ORDER BY Antal DESC" test.pcap
[
size = 10
{
"table_name": "result-0",
"query": "SELECT NAME( 'qtype' , qtype ) AS qt, COUNT(*) AS antal FROM dns GROUP BY qtype ORDER BY Antal DESC",
"head": [
{ "name": "qt","type": "text" },
{ "name": "antal","type": "int" }
],
"data": [
["A",8626],
["AAAA",680],
["PTR",566],
["TXT",202],
["SRV",113],
["MX",16],
["SOA",8],
["0",8],
["*",8],
["NS",8]
]
}
]
패킷 데이터를 SQL 쿼리문으로 추출해 낼 수 있다는 점에서 굉장히 유용한 부분이 있습니다. 아쉬운 점은 일부 프로토콜로만 한정되어 사용할 수 없지만 광범위하게 프로토콜을 지원한다면 정말 편할거 같은 느낌입니다. 예전에 DNS 프로토콜을 파싱하여 활용할 일이 있어서 찾다가 보게되었는데 나름 유용하게도 이용할 수 있겠다는 생각이 들었습니다.
SQL 쿼리로 패킷데이터를 조회한다는 점, 흥미롭지 않나요. 더 많은 예제는 해당 홈페이지 위키 페이지를 참고하시면 됩니다.
일단 다운로드 및 세부 정보는 다음 경로에서 확인할 수 있습니다:
https://github.com/dotse/packetq
주요한 기능을 보면 PCAP 파일을 빠르게 파싱할 수 있다는 것을 강점으로 내세우고 있고요 SQL 쿼리문 형태로 사용이 가능합니다. SQL 에 익숙하신 분에게는 더없이 좋겠죠. XML, CSV, JSON, Table 형태로도 데이터를 출력시킬 수 있습니다. 아직은 DNS 와 ICMP 프로토콜 정도만 지원하고 있다는 점입니다. 그리고 PacketQ 가 SQL 쿼리문 형태로 사용할 수 있다고 하지만 완벽한 SQL 문 형태로 지원하는 것은 아닙니다. 조인, 서브쿼리, distinct, like 같은것은 사용할 수 없습니다. 사용가능한 문법은 다음 페이지를 참고해 보시면 됩니다.
https://github.com/dotse/PacketQ/blob/master/src/grammar
간단한 사용방법을 몇개 살펴보겠습니다. SQL 쿼리 같이 * 로 해서 전체 데이터를 다 출력할 수도 있지만 특정필드로도 제한할 수 있습니다. 일단, DNS 데이터에서 추출해 낼 수 있는 데이터 형태는 다음과 같습니다.
{ "name": "id","type": "int" },
{ "name": "s","type": "int" },
{ "name": "us","type": "int" },
{ "name": "ether_type","type": "int" },
{ "name": "src_port","type": "int" },
{ "name": "dst_port","type": "int" },
{ "name": "src_addr","type": "text" },
{ "name": "dst_addr","type": "text" },
{ "name": "protocol","type": "int" },
{ "name": "ip_ttl","type": "int" },
{ "name": "fragments","type": "int" },
{ "name": "qname","type": "text" },
{ "name": "aname","type": "text" },
{ "name": "msg_id","type": "int" },
{ "name": "msg_size","type": "int" },
{ "name": "opcode","type": "int" },
{ "name": "rcode","type": "int" },
{ "name": "extended_rcode","type": "int" },
{ "name": "edns_version","type": "int" },
{ "name": "z","type": "int" },
{ "name": "udp_size","type": "int" },
{ "name": "qd_count","type": "int" },
{ "name": "an_count","type": "int" },
{ "name": "ns_count","type": "int" },
{ "name": "ar_count","type": "int" },
{ "name": "qtype","type": "int" },
{ "name": "qclass","type": "int" },
{ "name": "atype","type": "int" },
{ "name": "aclass","type": "int" },
{ "name": "attl","type": "int" },
{ "name": "aa","type": "bool" },
{ "name": "tc","type": "bool" },
{ "name": "rd","type": "bool" },
{ "name": "cd","type": "bool" },
{ "name": "ra","type": "bool" },
{ "name": "ad","type": "bool" },
{ "name": "do","type": "bool" },
{ "name": "edns0","type": "bool" },
{ "name": "qr","type": "bool" }
이중에서 qtype 과 opcode 를 출력하고 총 3개의 데이터로 제한을 한다면 다음과 같이 쿼리가 만들어질 수 있습니다. -s 뒤에 사용할 쿼리를 넣어주면 됩니다.
# ./packetq -s "select qname,opcode from dns limit 3" test.pcap
[
{
"table_name": "result-0",
"query": "select qname,opcode from dns limit 3",
"head": [
{ "name": "qname","type": "text" },
{ "name": "opcode","type": "int" }
],
"data": [
["n.search.naver.com.",0],
["n.search.naver.com.",0],
["n.search.naver.com.",0]
]
}
]
어떤 출발지에서 가장 많은 DNS 쿼리 요청을 했는지 출력해 봅니다.
# ./packetq -s "select src_addr,count(*) as count from dns group by src_addr order by count desc limit 1" sample.pcap
[
size = 2
{
"table_name": "result-0",
"query": "select src_addr,count(*) as count from dns group by src_addr order by count desc limit 1",
"head": [
{ "name": "src_addr","type": "text" },
{ "name": "count","type": "int" }
],
"data": [
["172.xx.xx.52",4]
]
}
]
만약 가장 많은 쿼리가 요청된 것을 뽑아낸다면 다음과 같이 합니다. qname 으로 groupby 를 해 줍니다. SQL 에 익숙하신 분들이라면 쿼리만 보고서도 쉽게 이해가 되실 것입니다. 하지만 SQL 경험이 없으신 분들은 group by 가 무엇인지도 혼란스러울 수도 있습니다. 여기서는 SQL 쿼리문 까지는 다루기 힘들기 때문에 의미만을 설명드리겠습니다. qname 으로 그룹을 만들어 주고 각 qname 마다의 카운트를 생성합니다. 그리고 정렬을 count 로 해서 가장 많은 숫자가 처음에 나오게 하고 limit 를 통해 출력데이터를 1개로만 합니다. 그 결과가 아래와 같습니다 :
# ./packetq -s "select qname,count(*) as count from dns group by qname order by count desc limit 1 " test.pcap
[
size = 1513
{
"table_name": "result-0",
"query": "select qname,count(*) as count from dns group by qname order by count desc limit 1 ",
"head": [
{ "name": "qname","type": "text" },
{ "name": "count","type": "int" }
],
"data": [
["resolver3.xxxx[삭제].com.",238]
]
}
]
이번에는 쿼리타입을 출력해 볼까요? 쿼리타입이 기본적으로는 정수 값입니다. 그런데 정수값으로 보면 사람한테는 익숙하지 않습니다. 이때 NAME 펑션 기능을 이용해 쿼리타입(qtype)을 텍스트 형태로 출력시킬 수 있습니다. qtype 으로 그룹을 묶고 타입과, 카운트 정보를 뽑아서 가장 많은 순서대로 출력을 시킵니다.
# ./packetq -s "SELECT NAME( 'qtype' , qtype ) AS qt, COUNT(*) AS antal FROM dns GROUP BY qtype ORDER BY Antal DESC" test.pcap
[
size = 10
{
"table_name": "result-0",
"query": "SELECT NAME( 'qtype' , qtype ) AS qt, COUNT(*) AS antal FROM dns GROUP BY qtype ORDER BY Antal DESC",
"head": [
{ "name": "qt","type": "text" },
{ "name": "antal","type": "int" }
],
"data": [
["A",8626],
["AAAA",680],
["PTR",566],
["TXT",202],
["SRV",113],
["MX",16],
["SOA",8],
["0",8],
["*",8],
["NS",8]
]
}
]
패킷 데이터를 SQL 쿼리문으로 추출해 낼 수 있다는 점에서 굉장히 유용한 부분이 있습니다. 아쉬운 점은 일부 프로토콜로만 한정되어 사용할 수 없지만 광범위하게 프로토콜을 지원한다면 정말 편할거 같은 느낌입니다. 예전에 DNS 프로토콜을 파싱하여 활용할 일이 있어서 찾다가 보게되었는데 나름 유용하게도 이용할 수 있겠다는 생각이 들었습니다.
SQL 쿼리로 패킷데이터를 조회한다는 점, 흥미롭지 않나요. 더 많은 예제는 해당 홈페이지 위키 페이지를 참고하시면 됩니다.
P.S 참고로 ICMP 로 출력할 수 있는 데이터는 다음과 같습니다:
{ "name": "id","type": "int" },
{ "name": "s","type": "int" },
{ "name": "us","type": "int" },
{ "name": "ether_type","type": "int" },
{ "name": "src_port","type": "int" },
{ "name": "dst_port","type": "int" },
{ "name": "src_addr","type": "text" },
{ "name": "dst_addr","type": "text" },
{ "name": "protocol","type": "int" },
{ "name": "ip_ttl","type": "int" },
{ "name": "fragments","type": "int" },
{ "name": "type","type": "int" },
{ "name": "code","type": "int" },
{ "name": "echo_identifier","type": "int" },
{ "name": "echo_sequence","type": "int" },
{ "name": "du_protocol","type": "int" },
{ "name": "du_src_addr","type": "text" },
{ "name": "du_dst_addr","type": "text" },
{ "name": "desc","type": "text" }
아래와 같은 형태로 사용하면 되겠죠.
./packetq -s "select * from icmp limit 3"
2013년 9월 27일 금요일
마이크로소프트 Message Analyzer 공식 릴리즈
마이크로소프트사의 Message Analyzer 가 그동안 베타로 테스트 되다가 드디어 공식적으로 다운로드센터에 등록되었습니다. 이제는 로그인 필요없이 편하게 바로 다운로드 받아 사용하실 수가 있습니다. Message Analyzer 는 새로운 형태의 메시지 분석기로서 트래픽 뿐만 아니라 시스템 메시지 또한 분석 대상입니다. 로그파일이나 Trace 파일을 분석을 할 수 있다는 의미입니다.
과거 Network Monitor 는 네트워크 트래픽 분석기로서의 역할만을 담당하지만 Message Analyzer 는 트래픽외 통합적으로 이벤트도 분석할수 있다는 부분이 다릅니다. 베타때부터 써보긴 했지만 와이어샤크만큼 사용 방법이 직관적이지는 않은것 같습니다.
그래도 새로운 분석기가 하나 더 생겼다는 것은 좋은일입니다. Message Analyzer 만의 장점이 있을테니 말이죠. 다운로드는 다음의 경로에서 할 수 있습니다.
http://www.microsoft.com/en-us/download/details.aspx?id=40308
앞으로 Message Analyzer 에 대한 기능도 소개해 보도록 하겠습니다.

[참고]
1. MS사의 새로운 네트워크 분석기, Message Analyzer
2. 마이크로소프트 네트워크 모니터의 프로세스별 통신 상태
과거 Network Monitor 는 네트워크 트래픽 분석기로서의 역할만을 담당하지만 Message Analyzer 는 트래픽외 통합적으로 이벤트도 분석할수 있다는 부분이 다릅니다. 베타때부터 써보긴 했지만 와이어샤크만큼 사용 방법이 직관적이지는 않은것 같습니다.
그래도 새로운 분석기가 하나 더 생겼다는 것은 좋은일입니다. Message Analyzer 만의 장점이 있을테니 말이죠. 다운로드는 다음의 경로에서 할 수 있습니다.
http://www.microsoft.com/en-us/download/details.aspx?id=40308
앞으로 Message Analyzer 에 대한 기능도 소개해 보도록 하겠습니다.

[참고]
1. MS사의 새로운 네트워크 분석기, Message Analyzer
2. 마이크로소프트 네트워크 모니터의 프로세스별 통신 상태
2013년 9월 25일 수요일
차세대 패킷 포맷, Pcap-NG 를 알고싶다 _ 네번째 이야기
추석명절은 잘 보내셨는지요? 오늘은 드디어 Pcap-NG 포맷 소개의 마지막 글입니다. 여러가지 바쁜일들로 이제서야 마지막 편을 쓰게 되었습니다.
패킷파일을 쉽게 살펴볼 수 있는 것은 도구를 이용하는 방법입니다. 그 중에 대표적인 패킷 분석도구인 와이어샤크를 통해서 쉽게 차세대 패킷 포맷을 살펴볼 수 있습니다.
와이어샤크에서는 Pcap-NG 포맷을 지원하고 있습니다. 아직 완벽하게 해당 포맷을 100% 다 지원하지는 않지만 기본적인 사용에는 큰 문제가 없습니다. 와이어샤크의 제약을 보면
- 일부(SHB, IDB, PB, EPB, SPB) 블럭만 지원하고 있어 다른 블럭은 무시가 됩니다.
- 많은 옵션들이 완벽하게 구현되지 않았습니다.
- 싱글 세션만 지원합니다.
- 많은 파일에 대해서 생성 테스트가 더 되어야 하고 파일을 합치는 기능에서는 약간의 제약이 있습니다.
일단 기본적으로 패킷 정보를 보여주는 형태는 이전의 PCAP 과 같습니다. 다만 GUI 관점에서 추가적인 메뉴가 더 생겼는데, Statistics 에서 Address Resolution 과 Comments Summary 를 더 볼 수 있습니다.
Address Resolution 은 PCAP-NG 에서 제공하는 NRB 블럭에서 기록된 정보와 유사한 정보를 제공해 줍니다. 패킷 내에서 사용된 도메인 정보를 한눈에 쉽게 볼 수 있도록 보여주고 있습니다. 이 것은 NRB 와는 별개로 와이어샤크에서 제공해 주는 기능입니다.
다음은 Comments Summary 로 주석 정보와 캡쳐가 된 운영체제 정보및 이더넷 정보를 추가적으로 확인할 수 있습니다. 아래 그림의 하단을 보면 주석정보를 볼 수가 있는데요, 주석정보는 PCAP-NG 에서 기록되는 정보중에 하나 입니다. 이 정보를 통해 분석가는 단순한 패킷 정보외에 패킷덤프가 이뤄진 곳의 환경정보와 기타 정보를 추가적으로 얻을 수 있어 분석에 참고할 수 있습니다. 또한 주석정보는 여러 분석가가 의견을 교환하고 분석을 계속 이어갈 수 있게 해주는 다리 역할을 해주기도 합니다.
다시말하지만, 분석은 패킷 자체의 정보만으로도 이용될 수 있지만 그렇지 않은 경우도 많습니다. 이럴 경우에는 전반적인 정보를 가지고 문제를 풀어나가야 합니다.
PcapNG 파일을 간단하게 파싱하여 정보를 출력해 줄 수 있는 코드가 있습니다. ntartest 라는 것으로 다음의 경로에서 다운로드 받을 수 있습니다.
http://wiki.wireshark.org/Development/PcapNg?action=AttachFile&do=view&target=ntartest.c
NTAR(Network Trace Archival and Retrieval)은 Pcap-NG 이전에 사용되어 오던 이름의 형태이며 블럭별로 간단한 정보를 출력해 주므로 해당 포맷을 파싱하려는 개발자 분들에게 초기 접근시 도움을 줄 수 있습니다. 일단 소스코드가 짧으니까요 :-)
실행하면 다음과 같은 결과를 얻을 수 있습니다.
# ./ntartest test.pcapng
+++Working on file: test.pcapng
This machine is little-endian.
This section is little-endian.
00000000: Block #1, Type = Section Header Block (0a0d0d0a)
+++ blockLength % 4 = 0, no pad bytes
00000004: Reported Block Length 96 (00000060), Adjusted Block Length 96 (00000060), next block at offset 00000060
00000008: Remainder of Block Data (00000058) bytes
0000005c: Block #2, Type = Interface Description Block (00000001)
+++ blockLength % 4 = 0, no pad bytes
00000060: Reported Block Length 56 (00000038), Adjusted Block Length 56 (00000038), next block at offset 00000094
00000064: Remainder of Block Data (00000030) bytes
00000094: Block #3, Type = Name Resolution Block (00000004)
+++ blockLength % 4 = 0, no pad bytes
00000098: Reported Block Length 5240 (00001478), Adjusted Block Length 5240 (00001478), next block at offset 0000150c
0000009c: Remainder of Block Data (00001470) bytes
Oops: short read, should have read 5232 bytes, only read 4096.
패킷 포맷 구조가 달라졌지만 사용자가 느끼는 부분에서는 큰 차이가 없을수도 있습니다. 똑같이 패킷 정보를 보여주기 때문이죠. 다만, 패킷 포맷 자체가 이전보다 유연해 졌고 보다 많은 정보를 저장하고 있다는 점에서는 분명 다릅니다. 포맷 자체 관점에서 들여다 보면 이전과는 큰 차이가 있다는 것을 느낄 수 있습니다.
단언컨대, Pcap-NG 는 패킷파일을 저장하는데 유연하면서도 앞으로 많이 쓰이게 될 패킷 포맷중에 하나입니다 :-)
여기에 소개하기 위하여 테스트로 와이어샤크에서 데이터를 pcap-ng 포맷으로 저장해 보았습니다. 패킷파일 포맷 구조를 살펴보면 다음과 같이 구성되어 있습니다.

제일 처음 SHB 가 시작되고 IDB,NRB 가 이어지고 EPB 에 패킷 데이터가 계속 기록되면서 마지막에 ISB 가 존재합니다. 해당 패킷 로우파일을 직접 들여다 보겠습니다.
다음 그림은 xxd 를 이용해 바이너리 형태를 본 것입니다. 제일 처음 SHB 가 시작되고 6000 0000 이라는 블럭 길이가 보입니다. 기본적으로 각 블럭마다 블럭의 길이가 타입 다음에 위치하고 블럭의 마지막에 다시 나타나게 됩니다. 그러므로 블럭타입을 보고 그 다음 길이를 본 후 해당 값과 매치되는 곳 까지 따라가게 되면 일반적으로 쉽게 블럭을 구분할 수 있습니다. 각 바이너리 값들은 우측에 추가적으로 기술해 놓았습니다.
다음 화면은 패킷파일의 제일 끝 부분입니다. EPB 가 시작된 이후로는 패킷데이터인 EPB 블럭이 계속 이어지다가 마지막에 인터페이스 상태정보인 ISB 가 나오게 됩니다. 블럭타입의 시작은 0500 0000 이 보이실 것이고요 6c00 0000 의 블럭크기값이 보입니다. 이후 옵션 헤더에 0100 으로 주석임을 알려주고 옵션길이인 1c00 만큼 (28 바이트) 의 주석 정보가 나오게 됩니다. dumpcap 에 의해서 카운트 정보가 제공되었다는 것입니다. 옵션 헤더는 추가적으로 다양하게 나타날 수 있습니다.
패킷 포맷을 직접 들여다 보면 어렵지는 않습니다. 이해가 쉽도록 보이는 내용 HEX 값 그대로를 표시해 놓았으니 비교해 보시고 블럭 포맷을 이해하시면 됩니다.
4회에 걸쳐서 Pcap-NG 포맷을 소개해 드렸습니다. 아직은 해당 포맷이 완벽하게 정립되지는 않았고 실험적으로 사용하는 블럭도 존재합니다. 하지만 와이어샤크에서는 이제 기본 패킷파일 저장 포맷으로 Pcap-NG 를 사용하고 있기 때문에 앞으로 이 포맷형태로 파일을 받아보는 경우가 많아질 것입니다.
실제 직접 패킷 포맷을 살펴볼 일은 많지 않습니다. 일일이 살펴보는 것도 쉽지 않고, 전문 분석도구들이 알아서 보기쉽게 표현해 주기 때문입니다. 하지만, 포맷 구조는 이렇게 만들어져 있다는 정도는 가볍게 봐 두시면 좋겠습니다. 아주 가끔은 패킷파일을 직접 복원해야 하는 경우가 생길수도 있고, 빠른 분석을 위해 여러분들이 직접 패킷파일을 분석하는 프로그램을 만들어야 하는 경우가 있을지도 모르거든요.
다음번에 여유가 된다면 PCAP 과 PCAP-NG 를 더 보기 쉽게 문서로도 만들어 보도록 하겠습니다. 아~ 그럴 날이 오겠죠 ? ^^
[참고]
1. 와이어샤크 PcapNg 개발현황
http://wiki.wireshark.org/Development/PcapNg
6. PCAP-NG 쉽게 들여다 보기
패킷파일을 쉽게 살펴볼 수 있는 것은 도구를 이용하는 방법입니다. 그 중에 대표적인 패킷 분석도구인 와이어샤크를 통해서 쉽게 차세대 패킷 포맷을 살펴볼 수 있습니다.
6-1. 와이어샤크를 통해 본 PCAP
와이어샤크에서는 Pcap-NG 포맷을 지원하고 있습니다. 아직 완벽하게 해당 포맷을 100% 다 지원하지는 않지만 기본적인 사용에는 큰 문제가 없습니다. 와이어샤크의 제약을 보면
- 일부(SHB, IDB, PB, EPB, SPB) 블럭만 지원하고 있어 다른 블럭은 무시가 됩니다.
- 많은 옵션들이 완벽하게 구현되지 않았습니다.
- 싱글 세션만 지원합니다.
- 많은 파일에 대해서 생성 테스트가 더 되어야 하고 파일을 합치는 기능에서는 약간의 제약이 있습니다.
일단 기본적으로 패킷 정보를 보여주는 형태는 이전의 PCAP 과 같습니다. 다만 GUI 관점에서 추가적인 메뉴가 더 생겼는데, Statistics 에서 Address Resolution 과 Comments Summary 를 더 볼 수 있습니다.
Address Resolution 은 PCAP-NG 에서 제공하는 NRB 블럭에서 기록된 정보와 유사한 정보를 제공해 줍니다. 패킷 내에서 사용된 도메인 정보를 한눈에 쉽게 볼 수 있도록 보여주고 있습니다. 이 것은 NRB 와는 별개로 와이어샤크에서 제공해 주는 기능입니다.
다음은 Comments Summary 로 주석 정보와 캡쳐가 된 운영체제 정보및 이더넷 정보를 추가적으로 확인할 수 있습니다. 아래 그림의 하단을 보면 주석정보를 볼 수가 있는데요, 주석정보는 PCAP-NG 에서 기록되는 정보중에 하나 입니다. 이 정보를 통해 분석가는 단순한 패킷 정보외에 패킷덤프가 이뤄진 곳의 환경정보와 기타 정보를 추가적으로 얻을 수 있어 분석에 참고할 수 있습니다. 또한 주석정보는 여러 분석가가 의견을 교환하고 분석을 계속 이어갈 수 있게 해주는 다리 역할을 해주기도 합니다.
다시말하지만, 분석은 패킷 자체의 정보만으로도 이용될 수 있지만 그렇지 않은 경우도 많습니다. 이럴 경우에는 전반적인 정보를 가지고 문제를 풀어나가야 합니다.
6-2. Pcap-NG 파일 리더
PcapNG 파일을 간단하게 파싱하여 정보를 출력해 줄 수 있는 코드가 있습니다. ntartest 라는 것으로 다음의 경로에서 다운로드 받을 수 있습니다.
http://wiki.wireshark.org/Development/PcapNg?action=AttachFile&do=view&target=ntartest.c
NTAR(Network Trace Archival and Retrieval)은 Pcap-NG 이전에 사용되어 오던 이름의 형태이며 블럭별로 간단한 정보를 출력해 주므로 해당 포맷을 파싱하려는 개발자 분들에게 초기 접근시 도움을 줄 수 있습니다. 일단 소스코드가 짧으니까요 :-)
실행하면 다음과 같은 결과를 얻을 수 있습니다.
# ./ntartest test.pcapng
+++Working on file: test.pcapng
This machine is little-endian.
This section is little-endian.
00000000: Block #1, Type = Section Header Block (0a0d0d0a)
+++ blockLength % 4 = 0, no pad bytes
00000004: Reported Block Length 96 (00000060), Adjusted Block Length 96 (00000060), next block at offset 00000060
00000008: Remainder of Block Data (00000058) bytes
0000005c: Block #2, Type = Interface Description Block (00000001)
+++ blockLength % 4 = 0, no pad bytes
00000060: Reported Block Length 56 (00000038), Adjusted Block Length 56 (00000038), next block at offset 00000094
00000064: Remainder of Block Data (00000030) bytes
00000094: Block #3, Type = Name Resolution Block (00000004)
+++ blockLength % 4 = 0, no pad bytes
00000098: Reported Block Length 5240 (00001478), Adjusted Block Length 5240 (00001478), next block at offset 0000150c
0000009c: Remainder of Block Data (00001470) bytes
Oops: short read, should have read 5232 bytes, only read 4096.
6-3. Pcap-NG 파일 RAW 로 들여다 보기
패킷 포맷 구조가 달라졌지만 사용자가 느끼는 부분에서는 큰 차이가 없을수도 있습니다. 똑같이 패킷 정보를 보여주기 때문이죠. 다만, 패킷 포맷 자체가 이전보다 유연해 졌고 보다 많은 정보를 저장하고 있다는 점에서는 분명 다릅니다. 포맷 자체 관점에서 들여다 보면 이전과는 큰 차이가 있다는 것을 느낄 수 있습니다.
단언컨대, Pcap-NG 는 패킷파일을 저장하는데 유연하면서도 앞으로 많이 쓰이게 될 패킷 포맷중에 하나입니다 :-)
여기에 소개하기 위하여 테스트로 와이어샤크에서 데이터를 pcap-ng 포맷으로 저장해 보았습니다. 패킷파일 포맷 구조를 살펴보면 다음과 같이 구성되어 있습니다.

제일 처음 SHB 가 시작되고 IDB,NRB 가 이어지고 EPB 에 패킷 데이터가 계속 기록되면서 마지막에 ISB 가 존재합니다. 해당 패킷 로우파일을 직접 들여다 보겠습니다.
다음 그림은 xxd 를 이용해 바이너리 형태를 본 것입니다. 제일 처음 SHB 가 시작되고 6000 0000 이라는 블럭 길이가 보입니다. 기본적으로 각 블럭마다 블럭의 길이가 타입 다음에 위치하고 블럭의 마지막에 다시 나타나게 됩니다. 그러므로 블럭타입을 보고 그 다음 길이를 본 후 해당 값과 매치되는 곳 까지 따라가게 되면 일반적으로 쉽게 블럭을 구분할 수 있습니다. 각 바이너리 값들은 우측에 추가적으로 기술해 놓았습니다.
각 포맷은 앞서 소개해 드린 포맷 구조에 맞춰서 보시면 쉽게 이해하실수가 있습니다. SHB 가 시작되고 IDB 가 이어지고 NRB 가 다시 나타납니다. NRB 는 블럭크기가 꽤 큽니다. 그래서 많은 내용이 반복되고 아래 그림과 같이 NRB 종료지점을 볼 수 있습니다. 종료지점은 쉽게 찾을 수 있겠죠? 새로 시작되는 블럭의 앞에 앞서 NRB 의 블럭길이인 7814 0000 을 찾으면 쉽게 찾을 수 있습니다. EPB 는 PCAP 구조와 같이 패킷 데이터를 저장하는 블럭입니다. 가장 많은 블럭의 분포를 보이게 되겠죠? EPB 의 블럭타입은 0600 0000 입니다. 그리고 크기는 6c00 0000 입니다. 다시 끝나는 위치는 6c00 0000 의 다음을 보면 0600 0000 으로 다시 EPB 가 시작이 됩니다. 그럼 첫 EPB 의 데이터는 무엇일까요? 잘 살펴보면 이더넷 타입을 가르키는 0800 과 IPv4 와 IP 헤더길이인 20 바이트를 뜻하는 4500 을 볼 수 있습니다. 직접 각 내용을 하나하나 뜯어보면 DNS 프로토콜임을 알 수가 있답니다. 계속 이어지는 것은 여러분의 몫으로 맡기겠습니다.
다음 화면은 패킷파일의 제일 끝 부분입니다. EPB 가 시작된 이후로는 패킷데이터인 EPB 블럭이 계속 이어지다가 마지막에 인터페이스 상태정보인 ISB 가 나오게 됩니다. 블럭타입의 시작은 0500 0000 이 보이실 것이고요 6c00 0000 의 블럭크기값이 보입니다. 이후 옵션 헤더에 0100 으로 주석임을 알려주고 옵션길이인 1c00 만큼 (28 바이트) 의 주석 정보가 나오게 됩니다. dumpcap 에 의해서 카운트 정보가 제공되었다는 것입니다. 옵션 헤더는 추가적으로 다양하게 나타날 수 있습니다.
패킷 포맷을 직접 들여다 보면 어렵지는 않습니다. 이해가 쉽도록 보이는 내용 HEX 값 그대로를 표시해 놓았으니 비교해 보시고 블럭 포맷을 이해하시면 됩니다.
7. 마지막
4회에 걸쳐서 Pcap-NG 포맷을 소개해 드렸습니다. 아직은 해당 포맷이 완벽하게 정립되지는 않았고 실험적으로 사용하는 블럭도 존재합니다. 하지만 와이어샤크에서는 이제 기본 패킷파일 저장 포맷으로 Pcap-NG 를 사용하고 있기 때문에 앞으로 이 포맷형태로 파일을 받아보는 경우가 많아질 것입니다.
실제 직접 패킷 포맷을 살펴볼 일은 많지 않습니다. 일일이 살펴보는 것도 쉽지 않고, 전문 분석도구들이 알아서 보기쉽게 표현해 주기 때문입니다. 하지만, 포맷 구조는 이렇게 만들어져 있다는 정도는 가볍게 봐 두시면 좋겠습니다. 아주 가끔은 패킷파일을 직접 복원해야 하는 경우가 생길수도 있고, 빠른 분석을 위해 여러분들이 직접 패킷파일을 분석하는 프로그램을 만들어야 하는 경우가 있을지도 모르거든요.
다음번에 여유가 된다면 PCAP 과 PCAP-NG 를 더 보기 쉽게 문서로도 만들어 보도록 하겠습니다. 아~ 그럴 날이 오겠죠 ? ^^
1. 와이어샤크 PcapNg 개발현황
http://wiki.wireshark.org/Development/PcapNg
2013년 9월 3일 화요일
차세대 패킷 포맷, Pcap-NG 를 알고싶다 _ 세번째 이야기
두번째 이야기에서 다루지 못한 2개의 블럭에 대한 설명입니다. Pcap-NG 를 설명하는데 생각보다 많은 시간이 필요하다 보니 먼저 작성한 내용을 공개하면서 어느덧 세번째 이야기 까지 왔습니다. 앞으로 네번째 이야기까지 다루면 Pcap-NG 소개는 끝입니다. :-)
이 블럭은 패킷 캡쳐를 하는 순간의 IP 주소와 연관된 도메인 이름을 지원해주는 역할을 합니다. 패킷캡쳐 파일안에 DNS Resolving 과 관련한 데이터들이 파일의 시작부분쯤에 위치하고 하고 있으며, Resolving 이 많은 경우 해당 블럭의 크기도 커지게 됩니다. 이 블럭의 장점은 Resolving 을 하면서 발생될 수 있는 Delay 를 줄여주며 캡쳐한 시점의 Resolving 정보를 가지고 있다는 것입니다. 예를들어, 캡쳐할 당시의 aaa.com 의 주소가 1.1.1.1 이었는데 분석할 당시에는 2.2.2.2 가 되어 있을수도 있습니다. 이런점에서 보면 유용하게 사용될 수 있는 부분이 있고, DNS 요청이 패킷파일에 많이 담겨있는 경우 Resolving 을 피할수도 있어 네트워크가 연결되어 있지 않더라도 정보를 얻을 수 있게됩니다.
블럭은 위와 같은 포맷으로 구성되어 있으며 블럭타입은 0x00000004 로 정의되어 있습니다. 블럭 타입 다음에 블럭에 대한 전체 길이가 나오고 각 레코드 정보가 나오게 되는데 레코드 타입은 아래와 같습니다.
각 레코드 값은 32비트로 구성되어 있으며 레코드 길이에 해당 도메인 이름의 실제 길이가 들어가게 됩니다. 마지막으로 옵션이 추가적으로 올 수 있는데, 옵션은 다음과 같습니다.
캡쳐에 사용된 인터페이스의 통계정보를 가지고 있습니다. 통계정보라 함은 인터페이스를 통해 받은 패킷 개수, 유실된 패킷개수등이 있습니다. 해당 블록에 정의된 Interface ID 값에 해당하는 인터페이스 정보를 기록하고, 통계정보를 담아야 하기에 보통 파일의 끝부분에 위치하게 되는 블럭입니다. 하지만, 같은 인터페이스에 여러번 반복되어 해당 블럭이 나타날수는 있습니다.
블럭타입은 0x00000005 를 사용하고 있고, 바로 다음 블럭의 전체길이가 나타납니다.그리고 통계정보를 담을 인터페이스 ID 번호가 위치합니다. 이 ID 는 IDB(Interface Description Block)에서 정의된 인터페이스중에 하나가 됩니다. 그 다음으로 EPB(Enhanced Packet Block)와 같은 타임스탬프가 정보가 나오며, 옵션에는 아래와 같은 정보가 들어가게 됩니다.
5-5. NRB(Name Resolution Block)
이 블럭은 패킷 캡쳐를 하는 순간의 IP 주소와 연관된 도메인 이름을 지원해주는 역할을 합니다. 패킷캡쳐 파일안에 DNS Resolving 과 관련한 데이터들이 파일의 시작부분쯤에 위치하고 하고 있으며, Resolving 이 많은 경우 해당 블럭의 크기도 커지게 됩니다. 이 블럭의 장점은 Resolving 을 하면서 발생될 수 있는 Delay 를 줄여주며 캡쳐한 시점의 Resolving 정보를 가지고 있다는 것입니다. 예를들어, 캡쳐할 당시의 aaa.com 의 주소가 1.1.1.1 이었는데 분석할 당시에는 2.2.2.2 가 되어 있을수도 있습니다. 이런점에서 보면 유용하게 사용될 수 있는 부분이 있고, DNS 요청이 패킷파일에 많이 담겨있는 경우 Resolving 을 피할수도 있어 네트워크가 연결되어 있지 않더라도 정보를 얻을 수 있게됩니다.
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 | Block Type = 0x00000004 |
+---------------------------------------------------------------+
4 | Block Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
8 | Record Type | Record Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12 / Record Value /
/ /* variable length, aligned to 32 bits */ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
. .
. . . . other records . . . .
. .
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Record Type == end_of_recs | Record Length == 00 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
/ /
/ Options (variable) /
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Block Total Length |
+---------------------------------------------------------------+
블럭은 위와 같은 포맷으로 구성되어 있으며 블럭타입은 0x00000004 로 정의되어 있습니다. 블럭 타입 다음에 블럭에 대한 전체 길이가 나오고 각 레코드 정보가 나오게 되는데 레코드 타입은 아래와 같습니다.
| 레코드 타입 이름 | 코드번호 | 설명 |
|---|---|---|
| nres_endofrecord | 0 | Name Resolution Record 가 끝남을 알려준다. |
| nres_ip4record | 1 | IPv4 주소에 해당하는 DNS 레코드 정보 |
| nres_ip6record | 2 | IPv6 주소에 해당하는 DNS 레코드 정보 |
각 레코드 값은 32비트로 구성되어 있으며 레코드 길이에 해당 도메인 이름의 실제 길이가 들어가게 됩니다. 마지막으로 옵션이 추가적으로 올 수 있는데, 옵션은 다음과 같습니다.
| 옵션 이름 | 코드번호 | 설명 |
|---|---|---|
| ns_dnsname | 2 | 이름 변환에 사용된 DNS 서버 이름 (UTF-8문자열) |
| ns_dnsIP4addr | 3 | DNS 서버의 IPv4 주소 |
| ns_dnsIP6addr | 4 | DNS 서버의 IPv6 주소 |
5-6. ISB(Interface Statistics Block)
캡쳐에 사용된 인터페이스의 통계정보를 가지고 있습니다. 통계정보라 함은 인터페이스를 통해 받은 패킷 개수, 유실된 패킷개수등이 있습니다. 해당 블록에 정의된 Interface ID 값에 해당하는 인터페이스 정보를 기록하고, 통계정보를 담아야 하기에 보통 파일의 끝부분에 위치하게 되는 블럭입니다. 하지만, 같은 인터페이스에 여러번 반복되어 해당 블럭이 나타날수는 있습니다.
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 | Block Type = 0x00000005 |
+---------------------------------------------------------------+
4 | Block Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
8 | Interface ID |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12 | Timestamp (High) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 | Timestamp (Low) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20 / /
/ Options (variable) /
/ /
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Block Total Length |
+---------------------------------------------------------------+
블럭타입은 0x00000005 를 사용하고 있고, 바로 다음 블럭의 전체길이가 나타납니다.그리고 통계정보를 담을 인터페이스 ID 번호가 위치합니다. 이 ID 는 IDB(Interface Description Block)에서 정의된 인터페이스중에 하나가 됩니다. 그 다음으로 EPB(Enhanced Packet Block)와 같은 타임스탬프가 정보가 나오며, 옵션에는 아래와 같은 정보가 들어가게 됩니다.
| 옵션 이름 | 코드번호 | 설명 |
|---|---|---|
| isb_starttime | 2 | 캡쳐가 시작된 시간으로, 4바이트 2개 블럭으로 시간이 저장된다. |
| isb_endtime | 3 | 캡쳐가 끝난 시간으로, 4바이트 2개 블럭으로 시간이 저장된다. |
| isb_ifrecv | 4 | 패킷 캡쳐가 시작된 이후로 물리적 인터페이스에서 받은 패킷 개수 |
| isb_ifdrop | 5 | 패킷 캡쳐가 시작된 이후로 컴퓨터 자원의 부족으로 유실된 패킷 개수 |
| isb_filteraccept | 6 | 패킷 캡쳐가 시작된 이후로 필터에 의해서 받은 패킷 개수 |
| isb_osdrop | 7 | 패킷 캡쳐가 시작된 이후로 운영체제에 의해서 유실된 패킷 개수 |
| isb_usrdeliv | 8 | 패킷 캡쳐가 시작된 이후로 사용자에게 전달된 패킷 개수 |
피드 구독하기:
글 (Atom)




