Edited at

CでEtherフレームを理解する

More than 1 year has passed since last update.


概要

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;
}