監看網路封包程式設計概說

以前同事寫的文章.
作者: 邵文琳
前一個禮拜花了不少時間在看網路封包方面的程式設計 ..覺得十分有興趣.
感謝charles 和 ellery 的幫助 . 讓我觀念比較清楚 …
在這裡我把我這一個星期的學習心得寫下來.
讓大家以後若有機會寫到類似程式時, 可是事半功倍 ..
網路程式設計方面相關資料很多 . 但是都是對比較上層的tcp,或udp的應用在做介紹.
對於網路層和datalink層面則著墨較少 .
所以”對我而言” 學習起來比較困難. 希望寫下這個對大家會有幫助.
一個網路封包監看程式 . 說穿了 只有四個重點:
第一 . 開啟一個可接收raw packet 的socket .
一般我們開始socket接收tcp或udp的packet 時, 我們收到的內容就直接是資料內容 .
kernel 己經幫我們把ethernet header 和 ip 或 arp header 都拿掉了.
但是在寫網路封包程式時, 我們需要header . 因此我們在開啟socket 的時候,就可以針對我們要收集的封包總類做過濾. 要求接收到的資料裡要包涵完整的封包標頭檔…
第二 . 有條件的接收封包和無條件的接收封包.
網路卡只會接收到ethernet header 的target mac 是自己的mac 時, 或接收到TARGET MAC 為 ff:ff:ff:ff:ff 的封包, 或是multicasting 的封包(也就是target ip是224開頭的封包 . 這種情況叫做 nonPromiscuous . 當我們要監看網路封包時,希望收到的不只是broadcast 或是 multicasting 或是自己的封包時, 我就要把網路卡設定成 Promiscuous . 如此一來我們就可以接到實體網路上的所有封包了…
第三 . filter 的設定.
當我們把網路卡設成 Promiscuous , 而且看到了所有封包header 時, 我們就可以依照我們自己的意思去過濾出我們想要的封包 . 可是一個問題來了 .. 網路上的封包那麼多.我們全都要一個個的去把它的標頭打開, 看看是不是自己要看的封包. 這樣做花的時間太長.以致於後面進來的封包都塞在buffer 裡, 等你一個個的檢查..我們需要一個filter 建立在硬體層和聯結層中建立一個filter , 先過濾出我們要的封包,再交由程式處理 . 這就是filter的功用.這種filter 是由bpf code 寫出來的! 不會寫bpf code ? 沒關係. 我們還是有別的方法把它變出來 ,,,,,
第四 . multiplexing I/O (blocking VS noblocking)
就如我們所知道的當我們在receving packet 時. 如果沒有資料進來, 程式就會等在那裡 ,這種情況叫做blocking , 但是有時候我們不想要無止限的等在那裡時, 郤又沒法把break 出來時,怎麼辦呢?使用signal 嗎? 另一種解決方式就是把資料流做unblocking.但unblocking 郤不能完成解決我的需求, 於是我利用到了 multiplexing I/O.接下來我在對以上四項做詳細解釋和範例:
第一 socket .
要如何開一個可以接收raw_data 的socket呢 ?

int sock;
// 開一個接收所有走ip protocol 封包的完整封包
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
// 開一個接收所有走arp protocol 封包的完整封包
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
// 開一個接收所有 protocol 封包的完整封包
sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_All));
// 在linux 下接收datalink 層的網路封包
sock = socket(PF_INET, SOCK_PACKET, htons(ETH+P_ALL));

PF_PACKET 支援兩個socket type:SOCK_RAW 和 SOCK_DGRAM, SOCK_RAW 會留下完整的 ethernet 標頭.SOCK_DGRAM 會去掉ETHERNET 標頭.但在linux 下使用PF_SOCK , SOCK_RAW 會有問題.最好是使用 PF_INET, SOCKET_PACKET 也可以留下完整的實體層的標頭.
第二 Promiscuous.
網路卡預設當然都是nonPromiscuous , 要變成 Promiscuous 必須對網卡做設定.在socket 己經open 下

#include <linux/in.h>
#include <linux/if_ether.h>
struct ifreq ifrq;
strncpy(ethreq.ifr_name,”eth0″,IFNAMSIZ);
ioctl(sock,SIOCGIFFLAGS,&ifrq);
ifrq.ifr_flags|=IFF_PROMISC;
ioctl(sock,SIOCSIFFLAGS,&ifrq);

若在打開之後,沒有在程式結束前關閉. 它就會一直打開哦 . 所以記得要關閉它.

strncpy(ethreq.ifr_name,”eth0″,IFNAMSIZ);
ioctl(sock,SIOCGIFFLAGS,&ifrq);
ifrq.ifr_flags&=~IFF_PROMISC;
ioctl(sock,SIOCSIFFLAGS,&ifrq);

第三. 設filter
filter 的設定主要是使用在實體層和連接層中間 . 用一種近似組語的bpf code 組成.寫起來挺麻煩的 . 所以我們直接使用tcpdump -dd 來產生的binary code 來用 .

for example:
tcpdump -dd ip dst 172.16.0.3
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000800 },
{ 0x20, 0, 0, 0x0000001e },
{ 0x15, 0, 1, 0xac100003 },
{ 0x6, 0, 0, 0x00000044 },
{ 0x6, 0, 0, 0x00000000 },
tcpdump -dd ip src 172.16.0.3
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000800 },
{ 0x20, 0, 0, 0x0000001a },
{ 0x15, 0, 1, 0xac100003 },
{ 0x6, 0, 0, 0x00000044 },
{ 0x6, 0, 0, 0x00000000 },
tcpdump -dd arp dst 172.16.0.3
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 3, 0x00000806 },
{ 0x2 0, 0, 0, 0x00000026 },
{ 0x15, 0, 1, 0xac100003 },
{ 0x6, 0, 0, 0x00000044 },
{ 0x6, 0, 0, 0x00000000 },

其實這些code 的產生是有跡可找的..我就把下面程式碼的binary code 原始bpf pesude code 列出來,可以相對應比較看看

/*
udp and host 192.168.9.10 and src port 5000
(000) ldh [12]
(001) jeq #0x800 jt 2 jf 14
(002) ldb [23]
(003) jeq #0x11 jt 4 jf 14
(004) ld [26]
(005) jeq #0xc0a8090a jt 8 jf 6
(006) ld [30]
(007) jeq #0xc0a8090a jt 8 jf 14
(008) ldh [20]
(009) jset #0x1fff jt 14 jf 10
(010) ldxb 4*([14]&0xf)
(011) ldh [x + 14]
(012) jeq #0x1388 jt 13 jf 14
(013) ret #68
(014) ret #0
*/

我們可以依我們的需要去下command 給tcpdump -dd 幫我們產生binary code. tcpdump, 不但區分不同的protocol. 來源,目的地, even 標頭的第幾個byte,和要接收封包的大小, 都可以變化. 實在是太神奇了.
接下來我們要把code 整合到程式裡去 …
第四 . blocking VS noblocking multiplexing I/O
unblocking 最重要的意思是當有資料流進來時立刻就傳回. 當沒有資料流進來時, 也傳回0.於是我們就可以利用unblocking 和loop 來控制我們要的時間.
以下是程式的寫法:

(fcntl(fd ,F_GETFL) & O_NONBLOCKING) // 1 成功 0 失敗

但是把資料一點一滴的傳進來也並不是我們要的 . 我們還是希望傳進來的資料是一個個block 過的資料,只是當沒有資料進來時, 能夠不要永遠的等在那而己.這就是multiplexing I/O 的概念.當我們read 和recvfrom 一個 file handle 時, 我們可以設定如何沒有資料進來時我要等多久,就能回到程式繼續執行我們要做的動作.
以下是程式的寫法:

#include “sys/time.h”
#include “sys/select.h”
int sock;
struct timeval tv;
fd_set readfds;
sock = socket(…);
tv.tv_sec=1;
tv.tv_usec=0;
FD_ZERO(&readfds); // 清空file handle set.
FD_SET(sock,&readfds); // 加入一個file handle 到file handle set
FD_SET(xxx,&readfds); // 如果還有別的file handle 也要加入同一個
set做控制的話 ..
select(sock+1,&readfds,NULL,NULL,&tv); // 對 file handle set 設
定時間控制
if(FD_ISSET(sock,&readfds)){ // 依readfds的設定來看是否為
true , 依我們的
例出來說,程式會在這裡等上一秒鐘. 如果在這一秒裡有data流進來,
傳回true, 再交給recvfrom把data 接下來, 否則一秒鐘後傳回false
!
// recvfrom(….)
}

基本上把握這幾個主要的重點, 要變化出自己的sniffer 不是問題..剩下來比較討厭的就是把ip 轉成network address to host address..大小印地安,和 inetaddr, inet_addr, inet_aton, inet_ntoa …這些變來變去的死東西了.

ACPI 速記

閱讀 Linux Center 的兩篇有關電源管理 ACPI 文章時的速記.
這兩篇文章的網址如下:
電源管理在Linux系統上的應用
電源管理在Linux系統上的應用 II
ACPI
– 偵測 MB 溫度,fan轉速,電壓,電池用量
– 2.0 須週邊配合!! MB, Power, 顯示卡,網路卡,周邊…
Linux ACPI driver 有維護 bad BIOS List
APM -> check /proc/apm or “dmesg | grep apm”
apmd daemon
apm 控制用及顯示
xapm 給 XWindow 用的
redhat 9 基本上沒有支援 ACPI
Suse 有,開機參數可以指定
acpi=off 不用
acpi=oldboot 只支援部份
pci=acpi
pci=noacpi
acpi=force 強制啟動
check /proc/acpi
acpid daemon
acpictl 控制
pmtools package
xset -dpms 關閉 energy star 並修改 XF86Config-4 中的 dpms

Debian 2.2r6 設定

雖說是 Debian 2.2r6 設定,但我想 Debian 的設定應該就是這樣了吧~~
1.網路
主機名稱:/etc/hostname
網路卡為 dhcp 或 static : /etc/network/interfaces

iface eth0 inet dhcp

or

iface eth0 inet static
address 192.168.0.1
netmask 255.255.255.0

2.initscript
在 /etc/init.d 下建立 script
再使用 update-rc.d xxxx defaults 99 99 > /dev/nul
也有 update-inetd –add 針對 inetd 增加服務

ImageMagick

某天從 IBM 電子報上看到這篇http://www-106.ibm.com/developerworks/library/l-graf2/?ca=dgr-lnxw02GraphicsLine,才知道 ImageMagick 這麼神奇,直接用指令列就可以做很多圖片編輯的工作了~
把圖片的右上角弄成圓角

composite -gravity NorthEast rounded-ne.png lake.png lake-1.png

rounded-ne.png 是一個圓角的圖案,背景設成白色,圓角部份則設為透明.
composite 會將 rounded-ne.png 與 lake.png 結合,並輸出到 lake-1.png
是故,同理可證,你可以搞定四個角

composite -gravity NorthEast rounded-ne.png lake.png lake-1.png
composite -gravity NorthWest? rounded-nw.png lake-1.png lake-2.png
composite -gravity SouthEast? rounded-se.png lake-2.png lake-3.png
composite -gravity SouthWest? rounded-sw.png lake-3.png lake-4.png

寫成 script 就變成了:

for img in *.png do composite -gravity NorthEast rounded-ne.png $img.png $img-1.png
composite -gravity NorthWest rounded-nw.png $img-1.png $img-2.png
composite -gravity SouthEast rounded-se.png $img-2.png $img-3.png
composite -gravity SouthWest rounded-sw.png $img-3.png $img-4.png
done

讓圖片浮起來,變成像按鈕的樣子

convert -raise 5×5 tree.png tree-raised.png

凹下去的話就是用

convert +raise 5×5 tree.png tree-lowered.png

加個邊框吧

convert -bordercolor red -border 5×5 flower.png flower-border.png

也是加框,不過有點巧妙…

convert -mattecolor black -frame 5×5 beach.png beach-frame.png
convert -mattecolor black -frame 5×5+2 beach.png beach-frame.png
convert -mattecolor black -frame 5×5+2+2 beach.png beach-frame.png

或是

convert -mattecolor gray -frame 25×25+0+25 rock.png rock-frame1.png
convert -mattecolor gray -frame 25×25+25+0 rock.png rock-frame2.png

把所有 .jpg 縮圖

for img in *.jpg
do
convert -sample 25%x25% $img thumb-$img
done

或是

mogrify -sample 25%x25% *.jpg
mogrify -format png -sample 25%x25% *.jpg

把 pdf 中的圖檔解出來

convert foo.pdf pages-%03d.png

of course, 也可以反過來,把圖檔作成 .pdf

convert dsc* foo.pdf

利用 ssh + tar 作備份

以前看網路上某篇文章時,節錄下來的,出處已經不可考了.它是利用 ssh + tar 來作備份,不僅安全,也兼顧了部分的效率.
$target 表示目的機器,指遠端機器
$backup_server 表示備份機器,同樣也是指遠端機器
$save 則是指備份目錄
要求遠端備份並拉回 local

ssh $target tar -cf – / –exclude /mnt | bzip2 -9 | cat > $target.tar.bz2

解開本地備份並回存到遠端去

bunzip2 -dc $target.tar.bz2 | ssh $target “cd /;tar -pxkf -“

備份 local 資料,丟到遠端去壓縮並存檔

tar cf – / –exclude xxx | ssh $backup_server “bzip2 -9 > $save/test.tar.bz2”

把遠端資料拉回 local,作解壓縮動作

ssh $backup_server “cat $save/test.tar.bz2” | bunzip2 | tar -xpkf –

Gentoo

因為前幾天,FAE 跟我說上海有個客戶用 gentoo, 結果我的程式執行有問題,雖然應該是可以執行,不過想說早就對 gentoo 蠻有興趣,所以就想說下載來玩玩看.
不過 .iso 檔案好幾天前就下載完,只是沒什麼時間來安裝,前幾天又卡到買房子的事情…昨天終於想說來玩玩看,於是就利用 vmware 試用版來裝裝看.
第一片 cd 放進去開機,就可以開出完整的 linux, 有點類似 knoppix.
只是要安裝到硬碟上,可就要花一番功夫了.
我參考了這篇 Gentoo 1.4 安裝指南 裡面的安裝步驟來做~~
我本來想說節省時間,從 stage 3 開始,可是一時手賤,改了 /etc/make.conf,結果反而多花了很多時間….這樣斷斷續續的一搞,花了很多時間~~最後卻又開不了機~~
只好再來一次,這一次,我才發現,原來我少了一些步驟,使得 gentoo 的 emerge 每次都得上網路去抓最新的 portage, 所以才會慢….這次就學聰明了,從光碟複製 portage ,然後盡量不改設定,這樣就快多了~~
安裝步驟大致摘錄如下:
1.光碟開機後,就是一個 live linux 可以用
2.切割你的 partition,自行格式化…等等.
3.把切割好的 partition 掛載到 /mnt/gentoo 下,並且把 swap 掛起來(swapon /dev/hdx),這樣速度會比較快
4.建議從stage 3開始,所以要
# cd /mnt/gentoo
# tar -xvjpf /mnt/cdrom/stages/stage3-*.tar.bz2
把 stage 3 的檔案放到 partition 中…
5.把光碟上的 portage 以及一些 gentoo 幫我們編譯好的檔案放到 /mnt/gentoo 中
# tar -xvjf /mnt/cdrom/snapshots/portage-yyyymmdd.tar.bz2 -C /mnt/gentoo/usr
# cp -R /mnt/cdrom/distfiles /mnt/gentoo/usr/portage/distfiles
# cp -a /mnt/cdrom/packages/ /mnt/gentoo/usr/portage/packages
6.再來就是 chroot, 切到剛剛都大致弄好的環境下
# mount -t proc proc /mnt/gentoo/proc
# cp /etc/resolv.conf /mnt/gentoo/etc/resolv.conf
# chroot /mnt/gentoo /bin/bash
# env-update
# source /etc/profile
7. 更新到最新套件(emerge sync), 調整 make 參數(/etc/make.conf), 更新系統(CONFIG_PROTECT=”-*” emerge -u system),省時間的話,這步驟可以跳過.
8.設定時區(從/usr/share/zoneinfo連結到/etc/localtime),以及設置 fstab.
9.安裝kernel, 若需要調整 kernel 參數,可以用 genkernel –config
# emerge -k gentoo-sources
# emerge -k genkernel
# genkernel
# emerge -k hotplug
# rc-update add hotplug default
10.安裝 log daemon, 有這幾種可以選: sysklogd, msyslog, syslog-ng metalog.
# emerge -k syslog-ng
# rc-update add syslog-ng default
11.安裝 cron daemon, 一樣也是有這幾種可選: dcron, fcron 和 vixie-cron
# emerge -k vixie-cron
# rc-update add vixie-cron default
12.檔案系統的相關工具: reiserfsprogs, xfsprogs, jfsutils, lvm-user
13.帳號,記得要設置 root 密碼,並且最好加入一個普通 user
# passwd
# useradd your_user -m -G users,wheel,audio -s /bin/bash
# passwd your_user
14.主機名稱以及網域名稱 /etc/hostname, /etc/dnsdomainname, /etc/hosts
15.網路設定 /etc/conf.d/net, 並把 eth0 加入開機順序中 rc-update add net.eth0 default , ethx 依此類推
16.調整鍵盤設定等, /etc/rc.conf
17. boot loader: grub 或 lilo,並修改 grub.conf 或 lilo.conf
# emerge -k grub
# grub
grub> root (hd0,0)
grub> setup (hd0)
18.把第二片的 portage 也放到硬碟中,並裝一些有的沒的
# mount /dev/cdrom /mnt/cdrom
# cp -a /mnt/cdrom/packages/* /usr/portage/packages/
# emerge -k xxxxx
19.最後的一些步驟,然後重開機啦
# etc-update
# exit
# reboot
🙂

[Tip]2004/02/12

postsuper -d msg-id 刪除卡在 queue 中的信 (postfix)
tree -dif | du 查看各目錄用量