地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

CでL2のペイロードを覗き見(wi)

概要

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に構造体が定義されています。

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表示しています。

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