Spring lecture
本記事ではPC初心者が一年間の勉強をし,先輩(@khwarizmi6514)の助けをいただきながら行ったパケット解析について書かれています.
経緯
一年間,本当にお世話になっている先輩に春休みの長期休みを使って,授業では教わることのなかなかできない「実践的な学び」について教えてもらうことになりました.週2日の時間を割いてもらいとても有意義な学びをしているので,この大事な経験を忘れないように今ここに記しています.
パケット処理実装
今回やった内容は パケットをKVMにて直つなぎし2台のVMを用意する. pingが通るのを確認したのち,自身が送ったicmpパケットをpcapで保存する. それについてL2,L3アドレスが確認できる程度のパケットpcap解析プログラムを作成する. というものです. パケットというのは,携帯のキャリアがよく広告としてうっている「パケ放題」のパケットと同じものです.基本情報技術者試験などで出てくるので,どういうものなのかは簡単には理解していました.しかし自分の手でそれを流して解析するというのはワクワクしました,
いざpingを打って見ても流れてる感じはありませんでした. pingを送った後,送り先のデバイスでキャプチャしてpcapfileに保存しました. pcap解析プログラムを書いていきました. そこで一個問題が発生しました. その問題は『byte order』についてです.
byte order
今回のパケット解析ではL2,L3の「length,dest&source addr,type,ttl」を表示させるものでした.しかしL2のtype,L3のアドレスは違う値が出てきてしまいました.なぜ違うのか?それは「リトルエンディアン」と「ビックエンディアン」というbyte orderの違いでした.
そこで ntohlとntohs(network to host longとnetwork to host short) htonlとhtons(host to network longとhost to network short) を使用して,正しい値にすることができました. 最後に「Wireshark」をつかってパケット解析を行いました.
code
まだ汚いコードですがお許しください.
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<pcap.h> #include<arpa/inet.h> #include<net/ethernet.h> #include<netinet/ip.h> #define PROTO_ETHER 1 #define PROTO_IP 2 void henkan(void *hen); //アドレスをprintさせる int print(void *addr,int proto){ switch(proto){ case PROTO_ETHER: printf("dest addr:\t"); for(int i=0;i<ETH_ALEN;i++){ printf("%02x ",((struct ether_header *)addr)->ether_dhost[i]); } printf("\nsource addr:\t"); for(int i=0;i<ETH_ALEN;i++){ printf("%02x ",((struct ether_header *)addr)->ether_shost[i]); } printf("\n"); printf("type:\t%04x\n",ntohs(((struct ether_header *)addr)->ether_type)); break; case PROTO_IP: henkan(addr); //printf("source addr:\t%02x\n",ntohl(((struct iphdr *)addr)->saddr)); //printf("dest addr:\t%02x\n",ntohl(((struct iphdr *)addr)->daddr)); printf("ttl:\t%x\n",((struct iphdr *)addr)->ttl); printf("\n"); break; default: break; } }; //byte order変換 void henkan(void *hen){ uint8_t a[4],b[4]; for(int i=0;i<4;i++){ a[i] = (uint8_t)(((ntohl(((struct iphdr *)hen)->saddr)) >> (24-i*8)) & 0b11111111); b[i] = (uint8_t)(((ntohl(((struct iphdr *)hen)->daddr)) >> (24-i*8)) & 0b11111111); } printf("source addr:\t%d.%d.%d.%d\ndest addr:\t%d.%d.%d.%d\n" ,a[0],a[1],a[2],a[3],b[0],b[1],b[2],b[3]); } int main(int argc, char **argv) { char fname[]="test.pcap"; pcap_t *p; char errbuf[PCAP_ERRBUF_SIZE]; int pnum=0; uint8_t *packet; struct pcap_pkthdr pkthdr; p = pcap_open_offline_with_tstamp_precision(fname, PCAP_TSTAMP_PRECISION_NANO, errbuf); struct ether_header *eth; struct iphdr *ip; while ((packet = (uint8_t *)pcap_next(p, &pkthdr))) { pnum++; printf("*** packet%d ***\n", pnum); printf("length: %d\n", pkthdr.caplen); eth = (struct ether_header *)packet; print(eth,PROTO_ETHER); eth++; ip=(struct iphdr *)eth; print(ip,PROTO_IP); } return 0; }
感想
パケット解析がうまく言った時はとても嬉しくて,同じ結果しか出てこないのに何度も試してしまいました. 先輩がいなかったらできない部分もありましたが自分の手で一個意味のあるプログラムをかけ実装できたのはとてもいい経験になりました. 実際に目に見えないものを自分のプログラムで可視化させて,一個一個書かれているいみを理解しながら読んでいけるのでとても為になりました. 先輩にはこうして春休みの時間を割いていただいて本当に感謝でいっぱいです. これからも新しい範囲のlectureも行ってくれるということで楽しみにしています.
お読みいただきありがとうございました。 何かありましたら下記のtwitterでDMをくださると幸いです。