nl80211のmulticastに耳を傾けてみる

nl80211 とは、netlink のうちの generic netlink の枠組みを使って作られているプロトコルです。libnl という標準的なライブラリが存在するのですが、内部で C の関数ポインタを使った callback が多用されていて、golang から扱いにくかったので pure golang でツールを書きました。ツールは `go get github.com/hkwi/nlgo` します。

まずは 802.11 でかつ nl80211 を使うドライバのデバイスを用意します（マシンに繋ぎます）。そして `nl80211grp` コマンド起動。

次で genl CTRL で nl80211 関連の multicast group id を調べています。nl80211 の中に config, scan, regulatory, mlme という multicast があることが分かります。

```
2015/02/16 11:52:32 "CTRL_ATTR(FAMILY_NAME: \"nl80211\\x00\", FAMILY_ID: 0x1e, VERSION: 0x1, HDRSIZE: 0x0, MAXATTR: 0xbf, OPS: [1: OP(ID: 0x1, FLAGS: 0xe), 2: OP(ID: 0x2, FLAGS: 0xb), 3: OP(ID: 0x5, FLAGS: 0xe), 4: OP(ID: 0x6, FLAGS: 0xb), 5: OP(ID: 0x7, FLAGS: 0xb), 6: OP(ID: 0x8, FLAGS: 0xb), 7: OP(ID: 0x9, FLAGS: 0xb), 8: OP(ID: 0xa, FLAGS: 0xb), 9: OP(ID: 0xb, FLAGS: 0xb), 10: OP(ID: 0xc, FLAGS: 0xb), 11: OP(ID: 0xe, FLAGS: 0xb), 12: OP(ID: 0xf, FLAGS: 0xb), 13: OP(ID: 0x10, FLAGS: 0xb), 14: OP(ID: 0x11, FLAGS: 0xe), 15: OP(ID: 0x12, FLAGS: 0xb), 16: OP(ID: 0x13, FLAGS: 0xb), 17: OP(ID: 0x14, FLAGS: 0xb), 18: OP(ID: 0x15, FLAGS: 0xf), 19: OP(ID: 0x16, FLAGS: 0xb), 20: OP(ID: 0x17, FLAGS: 0xb), 21: OP(ID: 0x18, FLAGS: 0xb), 22: OP(ID: 0x19, FLAGS: 0xb), 23: OP(ID: 0x1f, FLAGS: 0xa), 24: OP(ID: 0x1a, FLAGS: 0xb), 25: OP(ID: 0x1b, FLAGS: 0xb), 26: OP(ID: 0x1c, FLAGS: 0xa), 27: OP(ID: 0x1d, FLAGS: 0xb), 28: OP(ID: 0x21, FLAGS: 0xb), 29: OP(ID: 0x20, FLAGS: 0xc), 30: OP(ID: 0x4b, FLAGS: 0xb), 31: OP(ID: 0x4c, FLAGS: 0xb), 32: OP(ID: 0x25, FLAGS: 0xb), 33: OP(ID: 0x26, FLAGS: 0xb), 34: OP(ID: 0x27, FLAGS: 0xb), 35: OP(ID: 0x28, FLAGS: 0xb), 36: OP(ID: 0x2b, FLAGS: 0xb), 37: OP(ID: 0x2c, FLAGS: 0xb), 38: OP(ID: 0x2e, FLAGS: 0xb), 39: OP(ID: 0x30, FLAGS: 0xb), 40: OP(ID: 0x31, FLAGS: 0xb), 41: OP(ID: 0x32, FLAGS: 0xc), 42: OP(ID: 0x34, FLAGS: 0xb), 43: OP(ID: 0x35, FLAGS: 0xb), 44: OP(ID: 0x36, FLAGS: 0xb), 45: OP(ID: 0x37, FLAGS: 0xb), 46: OP(ID: 0x38, FLAGS: 0xb), 47: OP(ID: 0x39, FLAGS: 0xb), 48: OP(ID: 0x3a, FLAGS: 0xb), 49: OP(ID: 0x3b, FLAGS: 0xb), 50: OP(ID: 0x43, FLAGS: 0xb), 51: OP(ID: 0x3d, FLAGS: 0xb), 52: OP(ID: 0x3e, FLAGS: 0xa), 53: OP(ID: 0x3f, FLAGS: 0xb), 54: OP(ID: 0x41, FLAGS: 0xb), 55: OP(ID: 0x42, FLAGS: 0xb), 56: OP(ID: 0x44, FLAGS: 0xb), 57: OP(ID: 0x45, FLAGS: 0xb), 58: OP(ID: 0x49, FLAGS: 0xa), 59: OP(ID: 0x4a, FLAGS: 0xb), 60: OP(ID: 0x4f, FLAGS: 0xb), 61: OP(ID: 0x52, FLAGS: 0xb), 62: OP(ID: 0x51, FLAGS: 0xb), 63: OP(ID: 0x53, FLAGS: 0xb), 64: OP(ID: 0x54, FLAGS: 0xb), 65: OP(ID: 0x55, FLAGS: 0xb), 66: OP(ID: 0x57, FLAGS: 0xb), 67: OP(ID: 0x59, FLAGS: 0xb), 68: OP(ID: 0x5a, FLAGS: 0xb), 69: OP(ID: 0x5c, FLAGS: 0xb), 70: OP(ID: 0x5d, FLAGS: 0xb), 71: OP(ID: 0x5e, FLAGS: 0xb), 72: OP(ID: 0x5f, FLAGS: 0xa), 73: OP(ID: 0x60, FLAGS: 0xb), 74: OP(ID: 0x62, FLAGS: 0xb), 75: OP(ID: 0x63, FLAGS: 0xb), 76: OP(ID: 0x64, FLAGS: 0xa), 77: OP(ID: 0x65, FLAGS: 0xb), 78: OP(ID: 0x66, FLAGS: 0xb)], MCAST_GROUPS: [1: MCAST_GRP(GRP_ID: 0x7, NAME: \"config\\x00\"), 2: MCAST_GRP(GRP_ID: 0x8, NAME: \"scan\\x00\"), 3: MCAST_GRP(GRP_ID: 0x9, NAME: \"regulatory\\x00\"), 4: MCAST_GRP(GRP_ID: 0xa, NAME: \"mlme\\x00\")])"
```

ここで、nl80211 関連の group id 全てに参加します。こうすると、nl80211 の multicast が流れ込んでくることになります。

hostapd として起動させてみます。他の STA を接続し、解除すると次のようなシーケンスが出力されます。hostapd が association させるために投げている management frame の送信結果が漏れ聞こえてきます。そしておもむろに `NL80211_CMD_NEW_STATION` で新しい STA が接続されます。STA から接続解除すると `NL80211_CMD_DEL_STATION` が通知されます。

```
2015/02/16 11:52:45 NL80211_CMD_FRAME_TX_STATUS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, FRAME: []byte{0x50, 0x0, 0x3a, 0x1, 0x1c, 0xab, 0xa7, 0xf2, 0x13, 0x9d, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x10, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x0, 0x11, 0x4, 0x0, 0x8, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x32, 0x1, 0x8, 0x82, 0x84, 0x8b, 0x96, 0xc, 0x12, 0x18, 0x24, 0x3, 0x1, 0x1, 0x2a, 0x1, 0x4, 0x32, 0x4, 0x30, 0x48, 0x60, 0x6c, 0x7f, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40}, COOKIE: 0xffff8800b21fa500)
2015/02/16 11:52:45 NL80211_CMD_FRAME_TX_STATUS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, FRAME: []byte{0xb0, 0x0, 0x3a, 0x1, 0x1c, 0xab, 0xa7, 0xf2, 0x13, 0x9d, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x20, 0x28, 0x1, 0x0, 0x2, 0x0, 0xd, 0x0}, COOKIE: 0xffff8800b21fa000)
2015/02/16 11:52:45 NL80211_CMD_FRAME_TX_STATUS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, FRAME: []byte{0x50, 0x0, 0x3a, 0x1, 0x1c, 0xab, 0xa7, 0xf2, 0x13, 0x9d, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x30, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x64, 0x0, 0x11, 0x4, 0x0, 0x8, 0x73, 0x74, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x32, 0x1, 0x8, 0x82, 0x84, 0x8b, 0x96, 0xc, 0x12, 0x18, 0x24, 0x3, 0x1, 0x1, 0x2a, 0x1, 0x4, 0x32, 0x4, 0x30, 0x48, 0x60, 0x6c, 0x7f, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40}, COOKIE: 0xffff8800b21fa700)
2015/02/16 11:52:45 NL80211_CMD_FRAME_TX_STATUS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, FRAME: []byte{0xb0, 0x0, 0x3a, 0x1, 0x1c, 0xab, 0xa7, 0xf2, 0x13, 0x9d, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x40, 0x28, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0}, COOKIE: 0xffff8800ad64b400)
2015/02/16 11:52:45 NL80211_CMD_FRAME_TX_STATUS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, FRAME: []byte{0x10, 0x0, 0x3a, 0x1, 0x1c, 0xab, 0xa7, 0xf2, 0x13, 0x9d, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x50, 0x28, 0x11, 0x4, 0x0, 0x0, 0x1, 0xc0, 0x1, 0x8, 0x82, 0x84, 0x8b, 0x96, 0xc, 0x12, 0x18, 0x24, 0x32, 0x4, 0x30, 0x48, 0x60, 0x6c, 0x7f, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x40}, COOKIE: 0xffff8800b01c8900)
2015/02/16 11:52:45 NL80211_CMD_NEW_STATION attrs=NL80211_ATTR(IFINDEX: 0x7, MAC: []byte{0x1c, 0xab, 0xa7, 0xf2, 0x13, 0x9d}, GENERATION: 0x5)
2015/02/16 11:52:54 NL80211_CMD_DEL_STATION attrs=NL80211_ATTR(IFINDEX: 0x7, MAC: []byte{0x1c, 0xab, 0xa7, 0xf2, 0x13, 0x9d})
2015/02/16 11:52:55 NL80211_CMD_FRAME_TX_STATUS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, FRAME: []byte{0xc0, 0x0, 0x3a, 0x1, 0x1c, 0xab, 0xa7, 0xf2, 0x13, 0x9d, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x0, 0x2a, 0x2, 0x0}, COOKIE: 0xffff8801b35d1200)
2015/02/16 14:41:52 NL80211_CMD_FRAME_TX_STATUS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, FRAME: []byte{0xc0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x90, 0xf8, 0x3, 0x0}, COOKIE: 0xffff8800ab977d00)
```

次に hostapd を終了し、wpa_supplicant を起動させてみます。他の AP に接続し、解除すると次のようなシーケンスになります。

```
2015/02/16 14:45:36 NL80211_CMD_TRIGGER_SCAN attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, SCAN_SSIDS: [], SCAN_FREQUENCIES: [0: 0x96c, 1: 0x971, 2: 0x976, 3: 0x97b, 4: 0x980, 5: 0x985, 6: 0x98a, 7: 0x98f, 8: 0x994, 9: 0x999, 10: 0x99e, 11: 0x9a3, 12: 0x9a8, 13: 0x9b4])
2015/02/16 14:45:37 NL80211_CMD_NEW_SCAN_RESULTS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, SCAN_SSIDS: [], SCAN_FREQUENCIES: [0: 0x96c, 1: 0x971, 2: 0x976, 3: 0x97b, 4: 0x980, 5: 0x985, 6: 0x98a, 7: 0x98f, 8: 0x994, 9: 0x999, 10: 0x99e, 11: 0x9a3, 12: 0x9a8, 13: 0x9b4])
2015/02/16 14:45:37 NL80211_CMD_NEW_STATION attrs=NL80211_ATTR(IFINDEX: 0x7, MAC: []byte{0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0}, GENERATION: 0x7)
2015/02/16 14:45:37 NL80211_CMD_AUTHENTICATE attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, FRAME: []byte{0xb0, 0x0, 0x30, 0x1, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0, 0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0, 0xf0, 0x4a, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0})
2015/02/16 14:45:37 NL80211_CMD_ASSOCIATE attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, FRAME: []byte{0x10, 0x0, 0x30, 0x1, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0, 0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0, 0x0, 0x4b, 0x31, 0x4, 0x0, 0x0, 0x1, 0xc0, 0x1, 0x8, 0x82, 0x84, 0x8b, 0x96, 0x12, 0x24, 0x48, 0x6c, 0x32, 0x4, 0xc, 0x18, 0x30, 0x60, 0xdd, 0x18, 0x0, 0x50, 0xf2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x3, 0xa4, 0x0, 0x0, 0x27, 0xa4, 0x0, 0x0, 0x42, 0x43, 0x5e, 0x0, 0x62, 0x32, 0x2f, 0x0, 0x2d, 0x1a, 0xee, 0x11, 0x17, 0xff, 0xff, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3d, 0x16, 0xa, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdd, 0x1e, 0x0, 0x90, 0x4c, 0x33, 0xee, 0x11, 0x17, 0xff, 0xff, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdd, 0x1a, 0x0, 0x90, 0x4c, 0x34, 0xa, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4a, 0xe, 0x14, 0x0, 0xa, 0x0, 0x2c, 0x1, 0xc8, 0x0, 0x14, 0x0, 0x5, 0x0, 0x19, 0x0, 0x7f, 0x1, 0x1, 0xdd, 0x7, 0x0, 0xc, 0x43, 0x7, 0x0, 0x0, 0x0})
2015/02/16 14:45:37 NL80211_CMD_CONNECT attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, MAC: []byte{0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0}, STATUS_CODE: 0x0, RESP_IE: []byte{0x1, 0x8, 0x82, 0x84, 0x8b, 0x96, 0x12, 0x24, 0x48, 0x6c, 0x32, 0x4, 0xc, 0x18, 0x30, 0x60, 0xdd, 0x18, 0x0, 0x50, 0xf2, 0x2, 0x1, 0x1, 0x0, 0x0, 0x3, 0xa4, 0x0, 0x0, 0x27, 0xa4, 0x0, 0x0, 0x42, 0x43, 0x5e, 0x0, 0x62, 0x32, 0x2f, 0x0, 0x2d, 0x1a, 0xee, 0x11, 0x17, 0xff, 0xff, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3d, 0x16, 0xa, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdd, 0x1e, 0x0, 0x90, 0x4c, 0x33, 0xee, 0x11, 0x17, 0xff, 0xff, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0xdd, 0x1a, 0x0, 0x90, 0x4c, 0x34, 0xa, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4a, 0xe, 0x14, 0x0, 0xa, 0x0, 0x2c, 0x1, 0xc8, 0x0, 0x14, 0x0, 0x5, 0x0, 0x19, 0x0, 0x7f, 0x1, 0x1, 0xdd, 0x7, 0x0, 0xc, 0x43, 0x7, 0x0, 0x0, 0x0})
2015/02/16 14:45:37 NL80211_CMD_REG_CHANGE attrs=NL80211_ATTR(REG_INITIATOR: 0x5, REG_TYPE: 0x5, REG_ALPHA2: "JP\x00", WIPHY: 0x2)
2015/02/16 14:50:38 NL80211_CMD_TRIGGER_SCAN attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, SCAN_SSIDS: [], SCAN_FREQUENCIES: [0: 0x98a, 1: 0x98f, 2: 0x994, 3: 0x999, 4: 0x99e, 5: 0x9a3, 6: 0x9a8, 7: 0x9b4], SCAN_FLAGS: 0x1)
2015/02/16 14:50:40 NL80211_CMD_NEW_SCAN_RESULTS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, SCAN_SSIDS: [], SCAN_FREQUENCIES: [0: 0x98a, 1: 0x98f, 2: 0x994, 3: 0x999, 4: 0x99e, 5: 0x9a3, 6: 0x9a8, 7: 0x9b4], SCAN_FLAGS: 0x1)
2015/02/16 14:50:40 NL80211_CMD_FRAME_TX_STATUS attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, WDEV: 0x200000001, FRAME: []byte{0xd0, 0x0, 0xda, 0x0, 0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0, 0xa0, 0xff, 0x4, 0x0, 0x48, 0x1, 0x0}, COOKIE: 0xffff8800ad7c6c00)
2015/02/16 14:51:34 NL80211_CMD_DEL_STATION attrs=NL80211_ATTR(IFINDEX: 0x7, MAC: []byte{0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0})
2015/02/16 14:51:34 NL80211_CMD_DEAUTHENTICATE attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7, FRAME: []byte{0xc0, 0x0, 0x0, 0x0, 0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0, 0x10, 0x6f, 0x3f, 0xb0, 0x88, 0xfa, 0x0, 0x22, 0xcf, 0xe7, 0x6f, 0xf0, 0x0, 0x0, 0x3, 0x0})
2015/02/16 14:51:34 NL80211_CMD_DISCONNECT attrs=NL80211_ATTR(WIPHY: 0x2, IFINDEX: 0x7)
2015/02/16 14:51:34 NL80211_CMD_REG_CHANGE attrs=NL80211_ATTR(REG_INITIATOR: 0x5, REG_TYPE: 0x5, WIPHY: 0x0)
2015/02/16 14:51:34 NL80211_CMD_REG_CHANGE attrs=NL80211_ATTR(REG_INITIATOR: 0x5, REG_TYPE: 0x5, REG_ALPHA2: "JP\x00")
```