sk_buff transport_header の変遷

`struct skbuff` は Linux Kernel networking code で最も利用される構造体の１つで、`include/linux/skbuff.h` に定義されています。

「最も利用される」ため、コピー時のオーバーヘッド削減など様々な改良が継続的に加えられているため、メンバー変数の型や名前も大いに変遷しています。

そのうちの各レイヤのヘッダの場所を示す変数の変遷を調べたのでメモ。

# v2.6.0 ~ v2.6.21

```c
	union {
		struct tcphdr	*th;
		struct udphdr	*uh;
		struct icmphdr	*icmph;
		struct igmphdr	*igmph;
		struct iphdr	*ipiph;
		struct ipv6hdr	*ipv6h;
		unsigned char	*raw;
	} h;

	union {
		struct iphdr	*iph;
		struct ipv6hdr	*ipv6h;
		struct arphdr	*arph;
		unsigned char	*raw;
	} nh;

	union {
	  	unsigned char 	*raw;
	} mac;
```

この時代は各レイヤ事にヘッダへのポインタが union されていました。
実は v2.6.21 ~ v2.6.22 の間はいきなり変更されたのではなく、いくつもの commit により少しずつunion内のメンバーが減っていき、最終的に `*raw` だけになり、v2.6.22 以降の `sk_buff_data_t` になるという変遷をたどっています。
全てを列挙するのはあきらめましたので一部だけご紹介します。

* [SK_BUFF]: Introduce ip_hdr(), remove skb->nh.iph
    * [eddc9ec53be2ecdbf4efe0efd4a83052594f0ac0](https://github.com/torvalds/linux/commit/eddc9ec53be2ecdbf4efe0efd4a83052594f0ac0)
* [SK_BUFF]: Introduce arp_hdr(), remove skb->nh.arph
    * [d0a92be05ed4aea7d35c2b257e3f9173565fe4eb](https://github.com/torvalds/linux/commit/d0a92be05ed4aea7d35c2b257e3f9173565fe4eb)
* [SK_BUFF]: Introduce ipv6_hdr(), remove skb->nh.ipv6h
    * [0660e03f6b18f19b6bbafe7583265a51b90daf36](https://github.com/torvalds/linux/commit/0660e03f6b18f19b6bbafe7583265a51b90daf36)

ここまでで `union { ... } hn;` のメンバーは `*raw` １つになります。

```c
 	union {
 -		struct ipv6hdr	*ipv6h;
  		unsigned char	*raw;
  	} nh;
```

* [SK_BUFF]: unions of just one member don't get anything done, kill them
    * [b0e380b1d8a8e0aca215df97702f99815f05c094](https://github.com/torvalds/linux/commit/b0e380b1d8a8e0aca215df97702f99815f05c094)
    * Renaming skb->h to skb->transport_header, skb->nh to skb->network_header and
skb->mac to skb->mac_header, to match the names of the associated helpers
(skb[_[re]set]_{transport,network,mac}_header)

ここで `union` から `unsigned char` となり、名前も変更されます。

```c
 -	union {
 -		unsigned char	*raw;
 -	} h;
 -
 -	union {
 -		unsigned char	*raw;
 -	} nh;
 -
 -	union {
 -	  	unsigned char 	*raw;
 -	} mac;
 -
 +	unsigned char		*transport_header;
 +	unsigned char		*network_header;
 +	unsigned char		*mac_header;
 ```

# v2.6.22 ~ v2.6.39 (last v2.6.x), v3.0 ~ v3.10

```c
#ifdef NET_SKBUFF_DATA_USES_OFFSET
typedef unsigned int sk_buff_data_t;
#else
typedef unsigned char *sk_buff_data_t;
#endif

	sk_buff_data_t		transport_header;
	sk_buff_data_t		network_header;
	sk_buff_data_t		mac_header;
```

このバージョンではポインタではなくオフセットを使うようになります。
（`NET_SKBUFF_DATA_USES_OFFSET` が定義されていない場合は従来通り `char` ポインタになります。）

* [SK_BUFF]: Use offsets for skb->{mac,network,transport}_header on 64bit architectures
    * [2e07fa9cd3bac1e28cfe3131ed86b053afb02fc9](https://github.com/torvalds/linux/commit/2e07fa9cd3bac1e28cfe3131ed86b053afb02fc9)

```c
 -	unsigned char		*transport_header;
 -	unsigned char		*network_header;
 -	unsigned char		*mac_header;
 +	sk_buff_data_t		transport_header;
 +	sk_buff_data_t		network_header;
 +	sk_buff_data_t		mac_header;
```
# v3.11 ~ v3.19, v4.0 ~ v4.5 (last v4.x as of 2016/04/22)

```c
	__u16			transport_header;
	__u16			network_header;
	__u16			mac_header;
```

現在は sk_buff構造体のサイズを小さくするために、sk_buff_data_t ではなく、`__u16` を使用しています。

* net: Use 16bits for \*_headers fields of struct skbuff
    * [1a37e412a0225fcba5587f24c0dfc7636efc8b69](https://github.com/torvalds/linux/commit/1a37e412a0225fcba5587f24c0dfc7636efc8b69)
    * In order to mitigate ongoing incresase in the size of struct skbuff use 16 bit integer offsets rather than pointers for inner_*_headers.

```c
 -	sk_buff_data_t		inner_transport_header;
 -	sk_buff_data_t		inner_network_header;
 -	sk_buff_data_t		inner_mac_header;
 -	sk_buff_data_t		transport_header;
 -	sk_buff_data_t		network_header;
 -	sk_buff_data_t		mac_header;
 +	__u16			inner_transport_header;
 +	__u16			inner_network_header;
 +	__u16			inner_mac_header;
 +	__u16			transport_header;
 +	__u16			network_header;
 +	__u16			mac_header;
```

# 参考リンク

変更が行われたコミットを発見するために以下手順で進めました。

* `git log <tag> | head -1` でtag(ex: v2.6.11)のコミット番号見つけて `http://github.com/torvalds/linux/blame/<commit>/<file>` で変更箇所の表示
    * 例： v2.6.22 : [https://github.com/torvalds/linux/blame/7dcca30a32aadb0520417521b0c44f42d09fe05c/include/linux/skbuff.h](https://github.com/torvalds/linux/blame/7dcca30a32aadb0520417521b0c44f42d09fe05c/include/linux/skbuff.h)
* tag間で複数の commit で行われた変更の場合blameでは見つからないので、tag間の`git log`を表示させ検索
    * 例：v2.6.21 と v2.6.22間の変更を調査

```shell-session
$ git log v2.6.21 | head -1
commit de46c33745f5e2ad594c72f2cf5f490861b16ce1
$ git log v2.6.22 | head -1
commit 7dcca30a32aadb0520417521b0c44f42d09fe05c
$ git log 94a05509a9e11806acd797153d03019706e466f1 7dcca30a32aadb0520417521b0c44f42d09fe05c > ../tools/git-log-v2.6.21-22.log
```

> *2016/04/22 追記*
> コミット番号だけでなく、以下例のようにv2.6.11, v3.14等のTag名でもファイルを確認する事が可能です。
> 
```
https://github.com/torvalds/linux/blame/<tag>/include/linux/skbuff.h
https://github.com/torvalds/linux/blame/v3.14/include/linux/skbuff.h
```

また、[Twitter: @hiroysato](https://twitter.com/hiroysato) さんにご紹介頂いた以下リンクを参考にしました。ありがとうございました。

* [インデントコミットで真犯人がわからなくなった場合の git blame](http://qiita.com/sonots/items/b6852db6638cda233bc8)