Rubyのioctl経由でessidを取得するテンプレートを使用して、ESSIDではなくBSSIDを取得したかったのです。ただし、C 開発者ではないので、理解できないことがいくつかあります。
私がこれまでに持っていたものは機能しません:( ...
注: 私の一部は、wireless.h のいくつかのコメントによると、BSSID はioctl を介してのみ設定できると考えているため、少し混乱しています。ただし、取得するioctlは存在します。それと、より中間的な C 型のイズム (構造体、共用体など ;)) をほとんど完全に理解していないことに加えて、私にはわかりません。
def _get_bssid(interface)
# Copied from wireless.h
# supposing a 16 byte address and 32 byte buffer but I'm totally
# guessing here.
iwreq = [interface, '' * 48,0].pack('a*pI')
sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
# from wireless.h
# SIOCGIWAP 0x8B15 /* get access point MAC addresses */
sock.ioctl('0x8B15', iwreq) # always get an error: Can't convert string to Integer
puts iwreq.inspect
end
そのため、当面は wpa_cli メソッドを使用して BSSID を取得していますが、IOCTL を使用することをお勧めします。
def _wpa_status(interface)
wpa_data = nil
unless interface.nil?
# need to write a method to get the src_sock_path
# programmatically. Fortunately, for me
# this is going to be the correct sock path 99% of the time.
# Ideas to get programmatically would be:
# parse wpa_supplicant.conf
# check process table | grep wpa_suppl | parse arguments
src_sock_path = '/var/run/wpa_supplicant/' + interface
else
return nil
end
client_sock_path = '/var/run/hwinfo_wpa'
# open Domain socket
socket = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0)
begin
# bind client domain socket
socket.bind(Socket.pack_sockaddr_un(client_sock_path))
# connect to server with our client socket
socket.connect(Socket.pack_sockaddr_un(src_sock_path))
# send STATUS command
socket.send('STATUS', 0)
# receive 1024 bytes (totally arbitrary value)
# split lines by \n
# store in variable wpa_data.
wpa_data = socket.recv(1024)
rescue => e
$stderr.puts 'WARN: unable to gather wpa data: ' + e.inspect
end
# close or next time we attempt to read it will fail.
socket.close
begin
# remove the domain socket file for the client
File.unlink(client_sock_path)
rescue => e
$stderr.puts 'WARN: ' + e.inspect
end
unless wpa_data.nil?
@wifis = Hash[wpa_data.split(/\n/).map\
{|line|
# first, split into pairs delimited by '='
key,value = line.split('=')
# if key is camel-humped then put space in front
# of capped letter
if key =~ /[a-z][A-Z]/
key.gsub!(/([a-z])([A-Z])/,'\\1_\\2')
end
# if key is "id" then rename it.
key.eql?('id') && key = 'wpa_id'
# fix key so that it can be used as a table name
# by replacing spaces with underscores
key.gsub!(' ','_')
# lower case it.
key.downcase!
[key,value]
}]
end
end
編集: これまでのところ、誰もこの質問に答えることができませんでした. とにかく、wpa メソッドの方が好きだと思います。なぜなら、wpa メソッドからより多くのデータを取得できるからです。とはいえ、私が言いたいことの 1 つは、誰かが wpa コードを使用している場合、wlan
ソケットを読み取るためにエスカレートされた特権が必要になることに注意してください。
EDIT^2 (完全なコード スニペット): @dasup のおかげで、システム ioctl を使用して bssid と essid を正しくプルするようにクラスをリファクタリングすることができました。(YMMV は、Linux ディストリビューションの実装、年齢、およびその他の不安定化の可能性を考慮しています。ただし、次のコード スニペットは 3.2 および 3.7 カーネルで動作します。)
require 'socket'
class Wpa
attr_accessor :essid, :bssid, :if
def initialize(interface)
@if = interface
puts 'essid: ' + _get_essid.inspect
puts 'bssid: ' + _get_bssid.inspect
end
def _get_essid
# Copied from wireless.h
iwreq = [@if, " " * 32, 32, 0 ].pack('a16pII')
sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
sock.ioctl(0x8B1B, iwreq)
@essid = iwreq.unpack('@16p').pop.strip
end
def _get_bssid
# Copied from wireless.h
# supposing a 16 byte address and 32 byte buffer but I'm totally
# guessing here.
iwreq = [@if, "\0" * 32].pack('a16a32')
sock = Socket.new(Socket::AF_INET, Socket::SOCK_DGRAM, 0)
# from wireless.h
# SIOCGIWAP 0x8B15 /* get access point MAC addresses */
sock.ioctl(0x8B15, iwreq) # always get an error: Can't convert string to Integer
@bssid = iwreq.unpack('@18H2H2H2H2H2H2').join(':')
end
end
h = Wpa.new('wlan0')