Linux の iptables で OUTPUT 方向のパケットに ToS を設定する

扱っているプロトコルによってはまれに IP ToS を設定してパケットを送出したくなることがあります。
使ってるデーモンが対応しているなら適切に設定、もしくはソースを書き換えられるなら setsockopt(SO_PRIORITY) してパケットを送り出すように改造するといいと思います。

この文書では、IP ToS を設定してパケットを送出できないデーモンのパケットを、iptables で ToS 変更して送り出す場合を考えたいと思います。

# お題: 外向きの ICMP パケットに IP Precedence 3 を設定する
cisco のルータとかだと policy-map の ```set ip precedence 3``` で表現されるやつです。


# 解1: iptables の mangle table の OUTPUT chain の TOS target で --set-tos する
https://linuxjm.osdn.jp/html/iptables/man8/iptables-extensions.8.html

> TOS
> このモジュールは IPv4 ヘッダーの Type of Service フィールド (上位ビットも含む) や IPv6 ヘッダーの Priority フィールドを設定する。 TOS は DSCP と ECN と同じビットを共有する点に注意すること。 TOS ターゲットは mangle テーブルでのみ有効である。
>
> --set-tos value[/mask]
>    mask で指定されたビットを 0 にし (下の「注意」を参照)、 value と TOS/Priority フィールド の XOR を取る。 mask が省略された場合は 0xFF とみなされる。 

TOS ターゲットの ```--set-tos``` が使えれば目的を達成できそうです。TOS ターゲットは mangle テーブルでのみ使えるので、mangle テーブルの OUTPUT chain (下図の OUTPUT PATH 内の conntrack の次) にひっかけてみます。

https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg

```--set-tos``` で指定する値についてですが、precedence は IP ToS/DSCP の上位 3 bit 部分なので、```0x60 = 0110 0000(2)``` を指定します。

```
# iptables -t mangle -A OUTPUT --protocol icmp -j TOS --set-tos 0x60
# iptables -L -n -t mangle
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
 
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
 
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
 
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
TOS        icmp --  0.0.0.0/0            0.0.0.0/0            TOS set 0x60/0xff
 
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/222651/0af7bee5-a898-fa36-ccf3-f42f0d9dc373.png)

こんな感じになります。今回は例として ICMP をひっかけましたが、お使いのデーモンがしゃべるプロトコルのポート番号などにひっかけるようにすれば TCP や UDP での応答にもひっかけられます。

# 解2: iptables の mangle table の OUTPUT chain で DSCP target で --set-dscp もしくは --set-dscp-class する

> DSCP
> このターゲットは、 IPv4 パケットの TOS ヘッダーにある DSCP ビットの値の書き換えを可能にする。 これはパケットを操作するので、 mangle テーブルでのみ使用できる。
> 
> --set-dscp value
>     DSCP フィールドの数値を設定する (10 進または 16 進)。 
> --set-dscp-class class
>     DSCP フィールドの DiffServ クラスを設定する。 

DSCP ターゲットでも同様に書き換えできます。ひっかけられるのが mangle テーブルだけというのも TOS ターゲットと同じです。

今回は IP Precedence 3 を示す DSCP クラス名 ```CS3``` で指定してみます

```
# iptables -t mangle -A OUTPUT --protocol icmp -j DSCP --set-dscp-class CS3
# iptables -L -n -t mangle
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
 
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
 
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
 
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DSCP       icmp --  0.0.0.0/0            0.0.0.0/0            DSCP set 0x18
 
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/222651/519c1a11-0e36-a9c8-4b0d-86a967157a6c.png)

```--set-tos``` の場合と ToS (DiffServ) フィールドの値は同じになります。