2012년 1월 19일 목요일

기가비트 환경에서 패킷덤프 손실을 줄여보기 위한 도구, GULP

기가비트 환경에서 손실없이 패킷을 잡아내기 위한 방법들은 무엇이 있을까? 여러가지 것들이 있을 수 있겠지만, 우리가 흔히 사용하고 있는 리눅스 시스템에서 특별한 변경없이 간단하게 사용할 수 있는 도구 하나를 소개해 볼까 한다. 도구 하나만으로 완벽한 손실없이 패킷을 잡아내기에는 여러가지 환경적 제약이 따른다. 그러므로, 최대한 손실을 줄이면서 패킷을 잡아낼 수 있는 방법을 고민해야 하는데, 스레드 기반의 GULP 가 도움이 되지 않을까 한다.

이 도구를 만든 배경 및 세부적인 정보는 다음 URL 에서 얻을 수 있다.

http://staff.washington.edu/corey/gulp/

비교적 작성된지가 오래되었으므로 이점을 감안하기 바란다.

컴파일은 간단히 make 를 하는 것 만으로도 어렵지 않게 결과를 얻어낼 수 있다.

# make
cc -O    check64bit.c   -o check64bit
./check64bit; rm -f check64bit


If output from Gulp is not compatible with tcpdump or wireshark,
Please see: http://staff.washington.edu/corey/gulp/gulp.html#64bit


cc -g -O gulp.c -o gulp -lpthread -lpcap

실행해 보면 아래와 같은 도움말 정보를 얻을 수 있다.

$ ./gulp
./gulp: Sending raw pcap data to a terminal is not a good idea.
        If you really want to do that, pipe ./gulp through cat but you
        probably want to redirect stdout to a file or another program instead.
        Perhaps you meant to pipe into 'tcpdump -r-' or 'ngrep -I-' ?

Usage: ./gulp [--help | options]
    --help      prints this usage summary
    supported options include:
      -d        decapsulate Cisco ERSPAN GRE packets (sets -f value)
      -f "..."  specify a pcap filter - see manpage and -d
      -i eth#|- specify ethernet capture interface or '-' for stdin
      -s #      specify packet capture "snapshot" length limit
      -r #      specify ring buffer size in megabytes (1-1024)
      -c        just buffer stdin to stdout (works with arbitrary data)
      -x        request exclusive lock (to be the only instance running)
      -X        run even when locking would forbid it
      -v        print program version and exit
      -Vx...x   display packet loss and buffer use - see manpage
      -p #      specify full/empty polling interval in microseconds
      -q        suppress buffer full warnings
      -z #      specify write blocksize (even power of 2, default 65536)
    for long-term capture
      -o dir    redirect pcap output to a collection of files in dir
      -C #      limit each pcap file in -o dir to # times the (-r #) size
      -W #      overwrite pcap files in -o dir rather than start #+1
    and some of academic interest only:
      -B        check if select(2) would ever have blocked on write
      -Y        avoid writes which would block

여러 패킷 덤프 도구들을 사용해 보았다면 옵션만 보아도 어렵지 않게 사용할 수 있다. 참고로 덤프되는 데이터를 바로 터미널에 출력하는 것은 권장 되지 않으므로, 파일로 리다이렉션 하거나 또는 tcpdump, ngrep 과 같은 프로그램으로 리다이렉션 하는 것이 좋다

다음예는 , -i 로 덤프할 인터페이스를 지정하고 '>' 로 파일로 저장하고 있다.

#./gulp -i eth0 > tt

덤프된 파일의 타입을 살펴보면 캡쳐파일임을 알 수 있고 tcpdump 프로그램으로 확인할 수 있다.

# file tt
tt: tcpdump capture file (little-endian) - version 2.4 (Ethernet, capture length 65535)
# tcpdump -r tt
reading from file tt, link-type EN10MB (Ethernet)

다시 패킷을 덤프하는 과정에서 중지하고 살펴보면 12만개 패킷 중 31 개의 패킷이 drop 되었다. 추가로 나오는 정보에서 이런 drop 되는 패킷을 줄이기 위하여 rmem_max 와  rmem_default 값을 늘려주기를 권장하고 있다.

참고로  이전에 작성한 다음 블로그 글을 참고하여 파라미터 값을 조정하면 도움이 될 것이다.

[링크] 커널에서 drop 되는 패킷 수가 많다면...

# ./gulp -i bond0 > tt2
^C
121313 packets captured
121343 packets received by filter
31 packets dropped by kernel

Note ./gulp may drop fewer packets if you increase:
  /proc/sys/net/core/rmem_max and
  /proc/sys/net/core/rmem_default
to 4194304 or more

ring buffer use: 1.8% of 100 MB

느낌상으로는 다른 캡쳐 도구에 비해 손실이 적어 보이기도 한다. 그래서 tcpdump 와 gulp 로 테스트를 해 보았다.

# tcpdump -i bond0 -n -s 1514 -w tt
tcpdump: listening on bond0, link-type EN10MB (Ethernet), capture size 1514 bytes
^C207292 packets captured
208468 packets received by filter
1176 packets dropped by kernel

# ./gulp -i bond0 -s 1514 > tt5
^C
240675 packets captured
240674 packets received by filter
0 packets dropped by kernel
ring buffer use: 0.8% of 100 MB

위 결과를 보면 tcpdump 에서는 1176 개의 패킷이 drop 된 것에 비해 gulp 에서는 한개의 패킷도 drop 되지 않았다.  간단한 결과만 보아도 단순 패킷덤프에서는 gulp 가 tcpdump 보다 우수하다.
gulp 에서 패킷이 유실되는 개수 및 RingBuffer 사용률을 같이 볼 수 있는데 -Vx 를 추가로 주고 덤프를 해 보면 된다.

# ./gulp -i bond0 -Vx > tt3
pkts dropped: 0, ring buf: 0.0%, max: 0.1%
pkts dropped: 0, ring buf: 0.0%, max: 0.1%
pkts dropped: 0, ring buf: 0.1%, max: 0.1%
pkts dropped: 0, ring buf: 0.0%, max: 0.1%
pkts dropped: 0, ring buf: 0.0%, max: 0.1%
pkts dropped: 0, ring buf: 0.0%, max: 0.4%
pkts dropped: 0, ring buf: 0.1%, max: 0.4%
pkts dropped: 0, ring buf: 0.1%, max: 0.4%
pkts dropped: 0, ring buf: 0.0%, max: 0.4%
pkts dropped: 0, ring buf: 0.0%, max: 0.4%
pkts dropped: 0, ring buf: 0.0%, max: 0.4%
pkts dropped: 0, ring buf: 0.1%, max: 0.4%
pkts dropped: 0, ring buf: 0.0%, max: 0.4%
pkts dropped: 0, ring buf: 0.0%, max: 0.4%
pkts dropped: 0, ring buf: 0.0%, max: 0.4%
pkts dropped: 0, ring buf: 0.1%, max: 0.4%
^Cpkts dropped: 0, ring buf: 0.1%, max: 0.4%

27455 packets captured
27454 packets received by filter
0 packets dropped by kernel
ring buffer use: 0.4% of 100 MB

패킷 데이터를 바로 파일로 저장하는 것 외에 프로그램으로 리다이렉션 하여 다음과 같이 사용할 수도 있다.

# ./gulp -i bond0 | tcpdump -r - -w tt4
# ./gulp -i bond0 | ngrep -I - www

그럼 어떤면에서 gulp 가 더 우수한 성능을 보여줄까 ? gulp 를 백그라운드로 실행하고 해당 프로세스를 찾아 strace 로 살펴보았다.

# ./gulp -i bond0 -s 1514 > tt5 &
[2] 5798
# ps -ef | grep gulp
root      5798 18450  0 14:32 pts/1    00:00:00 ./gulp -i bond0 -s 1514
root      6749 18450  0 14:33 pts/1    00:00:00 grep gulp
# strace -p 5798
Process 5798 attached - interrupt to quit
restart_syscall(<... resuming interrupted call ...>) = 0
nanosleep({0, 500000000}, NULL)         = 0
nanosleep({0, 500000000}, NULL)         = 0
nanosleep({0, 500000000}, NULL)         = 0
nanosleep({0, 500000000}, NULL)         = 0
nanosleep({0, 500000000}, NULL)         = 0
nanosleep({0, 500000000}, NULL)         = 0
nanosleep({0, 500000000}, NULL)         = 0
nanosleep({0, 500000000}, NULL)         = 0
nanosleep({0, 500000000}, ^C <unfinished ...>
Process 5798 detached

nanosleep 만이 계속 보인다. 무엇인가 계속 기록하고 해야 할텐데, nanosleep 만 보인다? 혹시 스레드를 사용하는 것일까? 그렇다. 다음과 같이 스레드 카운트를 확인해 보면 3개로 보인다.

# ps -o pid,comm,thcount 5798
  PID COMMAND         THCNT
 5798 gulp                3

이와 달리 같은 형태로  tcpdump 를 살펴보자.

# tcpdump -i bond0 -n -s 1514 -w tt6 &
[2] 9793
# tcpdump: listening on bond0, link-type EN10MB (Ethernet), capture size 1514 bytes

# ps -ef | grep tt6
root      9793 18450  0 14:35 pts/1    00:00:00 tcpdump -i bond0 -n -s 1514 -w tt6
root     10080 18450  0 14:35 pts/1    00:00:00 grep tt6

# strace -p 9793
write(4, "\245\1(\37\315Y\265\206\207\fa\315\257\26\1771u\265\240\223\30309A0\304\362\\\257\7k>~"..., 4096) = 4096
recvfrom(3, "\0\33!R\3334\0PV\250]5\10\0E\0\0(\v\"@\0\200\6Hzo\3\4\22\314\240g"..., 1514, MSG_TRUNC, {sa_family=AF_PACKET, proto=0x800, if10, pkttype=PACKET_HOST, addr(6)={1, 005056a85d35}, [18]) = 60
ioctl(3, SIOCGSTAMP, 0xbfffec18)        = 0
recvfrom(3, "\0PV\250]5\0\33!R\3334\10\0E\0\5\320w+@\0@\6\26\311\314\240g~o\3\4"..., 1514, MSG_TRUNC, {sa_family=AF_PACKET, proto=0x800, if10, pkttype=PACKET_OUTGOING, addr(6)={1, 001b2152db34}, [18]) = 1502
ioctl(3, SIOCGSTAMP, 0xbfffec18)        = 0
recvfrom(3, "\0PV\250]5\0\33!R\3334\10\0E\0\5\320w,@\0@\6\26\310\314\240g~o\3\4"..., 1514, MSG_TRUNC, {sa_family=AF_PACKET, proto=0x800, if10, pkttype=PACKET_OUTGOING, addr(6)={1, 001b2152db34}, [18]) = 1502
ioctl(3, SIOCGSTAMP, 0xbfffec18)        = 0
write(4, "R\370\377\3627\t\2\275\230\22G\321\27414\0172\0\341\224(\4rb\326\275\322\246\333\270\303e\200"..., 4096) = 4096
recvfrom(3, "\0\33!R\3334\0PV\250]5\10\0E\0\0(\v#@\0\200\6Hyo\3\4\22\314\240g"..., 1514, MSG_TRUNC, {sa_family=AF_PACKET, proto=0x800, if10, pkttype=PACKET_HOST, addr(6)={1, 005056a85d35}, [18]) = 60
ioctl(3, SIOCGSTAMP, 0xbfffec18)        = 0
recvfrom(3, ^C <unfinished ...>

무엇인가 아주 바쁘게 기록을 하며 움직이고 있다. 스레드 카운트를 확인 해보면 1 이다.

# ps -o pid,comm,thcount -p 9793
  PID COMMAND         THCNT
 9793 tcpdump             1

즉, GULP 는 스레드로 동작하여 파일 기록시에도 별도의 스레드로 처리하여 더 높은 성능을 나타내고 있는 것이다.

기가비트 네트워크에서는 트래픽 양이 워낙 많기 때문에 운영체제의 네트워크 파라미터를 최적화 하고, 적절한 패킷 캡쳐 도구를 선택해야 한다. 사용환경과 패킷캡쳐의 목적에 따라서 달라지겠지만, 자기에게 맞는 적절한 도구를 테스트해보고 선택하여 사용하면 될 것이다.

댓글 없음:

댓글 쓰기