[無線LAN信号分析]
 
[構成]
Raspberry Pi2
USB電源KSY UB310-0520
ケース
USB無線LANWLI-UC-G301N
USBハブU2H-TZ420SBK
USB HDDHDJ-UT2.0B

 
[機能概要]
  • 2.4GHz帯をとびかう無線LAN信号をキャプチャして、チャネルごとの無線の使用状況を分析する。
  • 無線LAN信号は、隣家の電波もお互い届いてしまいますが、1つのチャネルでは同時に1つのパケットしか送れないので、隣家と同じチャネルを使用していると、チャネルの通信権を取り合いながら通信する状況になります。無線LANのチャネルを何番にすべきか、無線LANの使用状況を確認するのに使います。

 
[tsharkのインストール]
  • 無線LAN信号のキャプチャには、Wiresharkのコンソール向けツールのtsharkを使います。USB無線LANは、モニタモードという特殊なモードでキャプチャ専用に使うため、Raspberry Piのネットワーク接続はLANケーブルを使います。
    
    pi@raspberrypi ~/wlan $ sudo apt-get update
    ..
    pi@raspberrypi ~/wlan $ sudo apt-get install tshark
    ..
    
  • Luaスクリプトを使えるように設定ファイル/etc/wireshark/init.luaの以下パラメータをfalseからtrueに変更します。
    
    run_user_scripts_when_superuser = true
    
[Luaスクリプト]
  • Wiresharkでは、あまり聞きなれないLuaという言語でスクリプトを実行することができます。詳細はWiresharkのHPhttps://www.wireshark.org/
  • Wiresharkがキャプチャしたパケットデータは、Fieldというパラメータから取得することができます。無線LAN信号強度は、「radiotap.dbm_antsignal」というfieldです。
  • Luaスクリプト上でfieldデータを取得するには、まずField.newで対象のfieldを抽出する関数を生成しておきます。次に、パケット処理関数の内部で、対象のfieldを抽出します。以下のスクリプトではtap.packet(pinfo,tvb)のなかで抽出しています。tap.packet()は、パケットキャプチャするたびに呼び出される関数です。
  • 抽出したデータは、FieldInfoというオブジェクトになりますが、対象のfieldが数値であったとしても、一旦tostringで文字列にしてから、tonumberで数値に戻さないとエラーになります。なぜかはよくわかりません。
  • FCS(Frame Check Sequence)を確認し、Bad FCSの場合はキャプチャデータがデータ化けしているので捨てます。
  • パケットの送受信アドレス情報には、「wlan.sa」「wlan.ta」「wlan.ra」「wlan.da」がありますが、「wlan.ta」「wlan.ra」がキャプチャした無線LANパケットの送信元(Transmitter)と受信先(Receiver)で、「wlan.sa」「wlan.da」はキャプチャした無線LANパケット以外を含めた、パケットの送信元(Source)、受信先(Destination)っぽいです。また、何れのfieldも必須情報ではなく、「wlan.ra」しか設定されないパケットもあります。
  • 各端末が使用しているアクセスポイントは、アクセスポイントから送信されるパケットのうち、制御フレームとデータフレーム(wlan.fc.type_subtypeが0x10以上)の、受信先アドレス「wlan.ra」から判断しています。このとき、マルチキャストMACアドレスを除外する必要があります。
  • 抽出した情報を、終了時に出力しますが、終了時に実行されるのがtap.draw()です。
  • 
    print("WiFi RSSI Analyzer")
    
    local macaddress_ssid = {}
    local macaddress_resolved = {}
    local rssi_ap = {}
    local rssi_other = {}
    local ssid_other = {}
    
    local field_radiotap_dbm_antsignal = Field.new("radiotap.dbm_antsignal")
    local field_radiotap_channel_freq = Field.new("radiotap.channel.freq")
    local field_radiotap_flags_badfcs = Field.new("radiotap.flags.badfcs")
    local field_wlan_mgt_ssid = Field.new("wlan_mgt.ssid")
    local field_wlan_fc_type_subtype = Field.new("wlan.fc.type_subtype")
    local field_wlan_sa = Field.new("wlan.sa")                   -- Source Address
    local field_wlan_ta = Field.new("wlan.ta")                   -- Transmitter Address (can be WiFi Access Point)
    local field_wlan_ta_resolved = Field.new("wlan.ta_resolved") -- Transmitter Address (can be WiFi Access Point)
    local field_wlan_ra = Field.new("wlan.ra")                   -- Receiver Address (can be WiFi Access Point)
    local field_wlan_da = Field.new("wlan.da")                   -- Destination Address
    
    local tap = Listener.new()
    local count = 0
    local subcount = 0
    local maccount = 0
    local ssidcount = 0
    local ch_freq = 0
    
    function tap.packet(pinfo,tvb)  -- Run for each captured packet.
      -- Get Field values --
      local radiotap_dbm_antsignal = field_radiotap_dbm_antsignal()
      local wlan_mgt_ssid = field_wlan_mgt_ssid()
      local wlan_fc_type_subtype = field_wlan_fc_type_subtype()
      local wlan_sa = field_wlan_sa()
      local wlan_ta = field_wlan_ta()
      local wlan_ta_resolved = field_wlan_ta_resolved()
      local wlan_ra = field_wlan_ra()
      local wlan_da = field_wlan_da()
      local radiotap_channel_freq = field_radiotap_channel_freq()
    
      if tostring(radiotap_channel_freq)~=nil then
        ch_freq = tonumber(tostring(radiotap_channel_freq))
      end
      
      if tostring(radiotap_flags_badfcs)~=nil then
        if tostring(radiotap_flags_badfcs)~="0" then
          print("Bad FCS.")
          return
        end
      end
    
      if tonumber(tostring(wlan_fc_type_subtype))~=nil and tostring(radiotap_dbm_antsignal)~=nil then
    --[[
        print("******START******")
        print(tostring(radiotap_dbm_antsignal))
        print(tostring(wlan_mgt_ssid))
        print(tostring(wlan_fc_type_subtype))
        print("wlan_sa " .. tostring(wlan_sa))
        print("wlan_ta " .. tostring(wlan_ta))
        print("wlan_ta_resolved " .. tostring(wlan_ta_resolved))
        print("wlan_ra " .. tostring(wlan_ra))
        print("wlan_da " .. tostring(wlan_da))
        print("*******END*******")
    ]]
        if tostring(wlan_fc_type_subtype)=="8" or tostring(wlan_fc_type_subtype)=="5" then
        -- Signal Strength vs Access Point --
          if wlan_ta~=nil then
            rssi_ap[tostring(wlan_ta)]=tostring(radiotap_dbm_antsignal)
            if wlan_mgt_ssid~=nil then
              macaddress_ssid[tostring(wlan_ta)]=tostring(wlan_mgt_ssid)
            else
              macaddress_ssid[tostring(wlan_ta)]=tostring(wlan_ta)
            end
            if wlan_ta_resolved~=nil then
              macaddress_resolved[tostring(wlan_ta)]=tostring(wlan_ta_resolved)
            end
          end
        else
        -- Connected Access Point vs Terminal MAC address --
          if tonumber(tostring(wlan_fc_type_subtype))>16 then  -- Access after Association
            local igbit = tonumber(string.sub(tostring(wlan_ra),2,2))
            if macaddress_ssid[tostring(wlan_ta)] and tostring(wlan_ra)~=nil and igbit~=nil then
              if igbit%2 == 0 and rssi_other[tostring(wlan_ra)]~=nil then -- Not Multicast MAC Address
                ssid_other[tostring(wlan_ra)]=macaddress_ssid[tostring(wlan_ta)]
              end
            end
          end
        -- Signal Strength vs Terminal MAC address --
          if wlan_ta~=nil then
            rssi_other[tostring(wlan_ta)]=tostring(radiotap_dbm_antsignal)
            if wlan_ta_resolved~=nil then
              macaddress_resolved[tostring(wlan_ta)]=tostring(wlan_ta_resolved)
            end
          end
        end
      end
      count=count+1
      subcount=subcount+1
      if subcount>=100 then
        maccount = 0;
        ssidcount = 0;
        for macadrs,rssi in pairs(rssi_other) do
          maccount = maccount + 1
        end
        for macadrs,ssid in pairs(ssid_other) do
          ssidcount = ssidcount + 1
        end
            print(tostring(count) .. " packets.\t" .. tostring(maccount) .. " Terminals. (" .. tostring(ssidcount) .. " AP info)")
        subcount=0
      end
    end
    
    function tap.draw(t)
      for macadrs,rssi in pairs(rssi_ap) do
        if macaddress_ssid[macadrs] and macaddress_resolved[macadrs] then
          print(tostring(ch_freq) .. ",AP," .. macadrs .. "," .. rssi .. "," .. macaddress_resolved[macadrs] .. "," .. macaddress_ssid[macadrs])
        elseif macaddress_resolved[macadrs] then
          print(tostring(ch_freq) .. ",AP," .. macadrs .. "," .. rssi .. "," .. macaddress_resolved[macadrs])
        elseif macaddress_ssid[macadrs] then
          print(tostring(ch_freq) .. ",AP," .. macadrs .. "," .. rssi .. "," .. macadrs .. "," .. macaddress_ssid[macadrs])
        else
          print(tostring(ch_freq) .. ",AP," .. macadrs .. "," .. rssi .. "," .. macadrs)
        end
      end
      for macadrs,rssi in pairs(rssi_other) do
        if macaddress_ssid[macadrs]==nil then
          if macaddress_resolved[macadrs] and ssid_other[macadrs] then
            print(tostring(ch_freq) .. ",Terminal," .. macadrs .. "," .. rssi .. "," .. macaddress_resolved[macadrs] .. "," .. ssid_other[macadrs] )
          elseif macaddress_resolved[macadrs] then
            print(tostring(ch_freq) .. ",Terminal," .. macadrs .. "," .. rssi .. "," .. macaddress_resolved[macadrs] )
          elseif ssid_other[macadrs] then
            print(tostring(ch_freq) .. ",Terminal," .. macadrs .. "," .. rssi .. "," .. macadrs .. "," .. ssid_other[macadrs] )
          end
        end
      end
    end
    
[実行結果]
  • channelはスクリプトで切り替えられないので、iwconfigで切り替えて、以下のようにスクリプトを実行します。この場合、スクリプトはhello.luaというファイル名で実行ディレクトリに置いておきます。-I optionで無線LANアダプタをモニタモードにします。-Y optionはデフォルトのtsharkの出力をさせないためのフィルタです。
  • キャプチャ中は、パケット数、検出した無線LAN端末数、そのうち使用しているアクセスポイントが判明した数、を100パケットごとに表示します。Ctrl+Cで停止させると、各アクセスポイント、無線LAN端末からの信号の、受信信号強度のリストを出力します。
    
    pi@raspberrypi ~/wlan $ sudo iwconfig wlan0 channel 1
    pi@raspberrypi ~/wlan $ sudo tshark -i wlan0 -I -X lua_script:hello.lua -Y "wlan.sa==00:00:00:00:00:00"
    tshark: Lua: Error during loading:
     [string "/usr/share/wireshark/init.lua"]:46: dofile has been disabled due to running Wireshark as superuser. See http://wiki.wireshark.org/CaptureSetup/CapturePrivileges for help in running Wireshark as an unprivileged user.
    WiFi RSSI Analyzer
    Running as user "root" and group "root". This could be dangerous.
    Capturing on 'wlan0'
    100 packets.    6 Terminals. (2 AP info)
    200 packets.    7 Terminals. (3 AP info)
    300 packets.    7 Terminals. (3 AP info)
    400 packets.    7 Terminals. (3 AP info)
    500 packets.    7 Terminals. (3 AP info)
    600 packets.    7 Terminals. (3 AP info)
    700 packets.    7 Terminals. (3 AP info)
    800 packets.    7 Terminals. (3 AP info)
    900 packets.    8 Terminals. (3 AP info)
    1000 packets.   8 Terminals. (3 AP info)
    1100 packets.   8 Terminals. (3 AP info)
    1200 packets.   9 Terminals. (3 AP info)
    ^C60 packets dropped
    0 packets captured
    2417,AP,e0:9d:b8:xx:xx:xx,-28,PlanexCo_xx:xx:xx,SSID-name
    2417,Terminal,00:1b:c7:xx:xx:xx,-60,Starvedi_xx:xx:xx
    2417,Terminal,24:fd:52:xx:xx:xx,-52,LiteonTe_xx:xx:xx,SSID-name
    2417,Terminal,14:7d:c5:xx:xx:xx,-52,MurataMa_xx:xx:xx
    2417,Terminal,70:18:8b:xx:xx:xx,-68,HonHaiPr_xx:xx:xx,SSID-name
    2417,Terminal,c8:ff:28:xx:xx:xx,-30,c8:ff:28:xx:xx:xx,SSID-name
    2417,Terminal,00:1b:c7:xx:xx:xx,-74,Starvedi_xx:xx:xx
    2417,Terminal,32:c5:42:xx:xx:xx,-64,32:c5:42:xx:xx:xx
    2417,Terminal,28:84:fa:xx:xx:xx,-66,28:84:fa:xx:xx:xx
    
 
 
TOP PAGE