#概要
EtherフレームはOSI参照モデルのレイヤー2に位置します。
直接通信する機器間でデータをやり取りする為の情報が入ったフレームです。
この記事ではC言語によるEtherフレームの解析を行い、Etherフレームについて理解を深めてみます。
#環境構築
CentOSを想定しています。
フレームの取得にはlibpcapというCライブラリを利用します。
yum -y update
yum install -y gcc vim wget tar flex byacc
wget http://www.tcpdump.org/release/libpcap-1.6.2.tar.gz
tar zxvf libpcap-1.6.2.tar.gz
cd libpcap-1.6.2
./configure
make -j 8
make install
#Etherフレームについて
##フレームフォーマット
Etherフレームの内容(IEEE802.3形式)は以下となっています。
概要 | サイズ | 説明 |
---|---|---|
プリアンブル | 7byte | タイミングを取るための固定ビット列 |
SFD | 1byte | タイミング用。 次のビットからデータが開始されるよ的な。 |
宛先MAC | 6byte | 通信先の機器を識別する為の番号 |
送信元MAC | 6byte | 通信元の機器を識別する為の番号 |
Etherタイプ | 2byte | 上位のプロトコルを表す値。 但し1500以下だったらデータサイズを表す |
データ | 可変 | カプセル化した中身のデータ |
FCS | 2byte | 受け取り側でエラーが発生してないかをチェックする用 |
###Etherタイプ(一部)
defineはnet/ethernet.hに定義されています。
以下はその値と説明についてです。
値 | define | 説明 |
---|---|---|
0x0000-0x05DC | - | フレームの長さ |
0x0800 | ETHERTYPE_IP | IPパケット |
0x0806 | ETHERTYPE_ARP | ARPパケット |
0x8100 | ETHERTYPE_VLAN | dot1Q VLANフレーム |
0x86DD | ETHERTYPE_IPV6 | IPv6パケット |
###Etherフレーム構造体
CでEtherフレームを扱う場合、net/ethernet.hに構造体が定義されています。
net/ethernet.h
struct ether_header
{
u_int8_t ether_dhost[ETH_ALEN]; /* destination eth addr */
u_int8_t ether_shost[ETH_ALEN]; /* source ether addr */
u_int16_t ether_type; /* packet type ID field */
} __attribute__ ((__packed__));
#EtherフレームのDump
フレームの受信処理はコチラを参照してください。
以下ではフレームを受信した後、コールバック関数内でEtherヘッダ内容をDump表示しています。
ether_hdr.c
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <string.h>
char * convmac_tostr(u_char *,char *,size_t);
void start_pktfunc( u_char *user, // pcap_loop関数の第4引数
const struct pcap_pkthdr *h , // 受信したPacketの補足情報
const u_char *p // 受信したpacketへのポインタ
){
char dmac[18] = {0};
char smac[18] = {0};
struct ether_header *eth_hdr = (struct ether_header *)p;
printf("ether header---------\n");
printf("dest mac %s\n",convmac_tostr(eth_hdr->ether_dhost,dmac,sizeof(dmac)));
printf("src mac %s\n",convmac_tostr(eth_hdr->ether_shost,smac,sizeof(smac)));
printf("ether type %x\n\n",ntohs(eth_hdr->ether_type));
}
char * convmac_tostr(u_char *hwaddr,char *mac,size_t size){
snprintf(mac,size,"%02x:%02x:%02x:%02x:%02x:%02x",
hwaddr[0],hwaddr[1],hwaddr[2],
hwaddr[3],hwaddr[4],hwaddr[5]);
return mac;
}