2012年2月25日土曜日

Linux で WPS を使う方法

STA(wext/nl80211) が Enrollee、AP(nl80211) が Registrar の場合、

■ STA 側
# cat wpa_supplicant.conf

ctrl_interface=/var/run/wpa_supplicant
update_config=1

wpa_supplicant 起動後
# wpa_cli wps_pbc (PBC の場合)
# wpa_cli wps_pin any 12345678 (PIN の場合)

■ AP 側 (hostapd)
# cat hostapd.conf
wpa=2
wpa_passphrase=xxxxxxxx
wps_state=2 (configured)
eap_server=1

hostapd 起動後
# hostapd_cli wps_pbc (PBC の場合)
# hostapd_cli wps_pin any 12345678 (PIN の場合)

WPS の実行と、configure 後の WPA2 による再接続は続けて行われる。
また、WPS 実行後、wpa_supplicant.conf が更新される。
(update_config=1 を書かなければ更新されない。)

# cat wpa_supplicant.conf
ctrl_interface=/var/run/wpa_supplicant
update_config=1

network={
        ssid="myssid"
        psk="xxxxxxxx"  # PSK ではなく、パスフレーズが書かれている
        proto=RSN
        key_mgmt=WPA-PSK
        pairwise=CCMP
        auth_alg=OPEN
}


2012年2月23日木曜日

カーネルモジュールのデバッグ

カーネルモジュールのデバッグ方法として、
printk + dmesg があるが、多くの文字列を出力するには適さない。

一つの方法として、proc ファイルシステムを使う方法がある。

■使い方
cat /proc/myprocentry
とすると、情報が読み出せる。

■ハンドラの登録
  struct proc_dir_entry *dirp;
  dirp = (struct proc_dir_entry *) create_proc_entry("/proc/myprocentry", 0444, (struct proc_dir_entry *) 0);
  dirp->read_proc = myproc_read;

これで、/proc/myprocentry に読み出しを行った際に、myproc_read が呼び出されるようになる。

wireless extensions

iwconfig などのコマンドは、内部で wireless extensions のインタフェースを使っている。
その先で、さらに呼ばれるのが、wireless_handlers である。

■wireless_handlers の登録
  mynet_dev = alloc_netdev(PRIV_SIZE, MYDEV_NAME, ether_setup);
  mynet_dev->netdev_ops = &mynet_dev_ops;
  mynet_dev->wireless_handlers = &mynet_wext_handlers;
  register_netdev(mynet_dev);

net_device の wireless_handlers に適切な関数を登録しておけば、
iwconfig から(ioctl 経由で)呼ばれる。

登録の仕方は、
static const iw_handler cfg80211_handlers[] = {
        [IW_IOCTL_IDX(SIOCGIWNAME)]     = (iw_handler) cfg80211_wext_giwname,
        [IW_IOCTL_IDX(SIOCSIWFREQ)]     = (iw_handler) cfg80211_wext_siwfreq,
        [IW_IOCTL_IDX(SIOCGIWFREQ)]     = (iw_handler) cfg80211_wext_giwfreq,
        [IW_IOCTL_IDX(SIOCSIWMODE)]     = (iw_handler) cfg80211_wext_siwmode,
        [IW_IOCTL_IDX(SIOCGIWMODE)]     = (iw_handler) cfg80211_wext_giwmode,
        [IW_IOCTL_IDX(SIOCGIWRANGE)]    = (iw_handler) cfg80211_wext_giwrange,
        [IW_IOCTL_IDX(SIOCSIWAP)]       = (iw_handler) cfg80211_wext_siwap,
        [IW_IOCTL_IDX(SIOCGIWAP)]       = (iw_handler) cfg80211_wext_giwap,
        [IW_IOCTL_IDX(SIOCSIWMLME)]     = (iw_handler) cfg80211_wext_siwmlme,
        [IW_IOCTL_IDX(SIOCSIWSCAN)]     = (iw_handler) cfg80211_wext_siwscan,
        [IW_IOCTL_IDX(SIOCGIWSCAN)]     = (iw_handler) cfg80211_wext_giwscan,
}
const struct iw_handler_def cfg80211_wext_handler = {
        .num_standard           = ARRAY_SIZE(cfg80211_handlers),
        .standard               = cfg80211_handlers,
};
など。詳しくは、/usr/src/linux/net/wireless/wext-compat.c を参照。

iwconfig を引数なしで実行するためには、少なくとも SIOCGIWNAME に対するハンドラの登録が必要。それ以外の iwconfig のコマンドを実行するためには、他の SIOC*IW ハンドラを実装する必要あり。

ifconfig

ネットワークインタフェースを追加し、ifconfig できるところまで行います。

■ネットワークインタフェースの確保
  struct net_device *mynet_dev;
  mynet_dev = alloc_netdev(PRIV_SIZE, NDEV_NAME, ether_setup);
  // PRIV_SIZE にプライベートデータを保存する領域のサイズ(int。特になければ 0)
  // NDEV_NAME にネットワークインタフェースの名前 (char *)

■ネットワークインタフェースの登録
  mynet_dev->netdev_ops = &mynet_dev_ops;
  register_netdev(mynet_dev);

これで、カーネルにネットワークインタフェースの登録が完了する。
ただし、最低限、下記の netdev_ops を定義しておかなければならない。

static struct net_device_ops mynet_dev_ops = {
  .ndo_init = mynet_init,
  .ndo_open = mynet_open,
  .ndo_start_xmit = mynet_xmit,
};

.ndo_open が ifconfig を実行したとき呼ばれる関数である。
また、.ndo_init は reigster_netdev() 実行時に呼ばれる関数、
ndo_start_xmit は、パケット送信時に呼ばれる関数である。

カーネルモジュールの書き方

モジュール名 moduletest のモジュールを作ります。



■使い方
(モジュールの読み込み)
# insmod moduletest.ko
(モジュールの取り外し)
# rmmod moduletest


■C コード
#include <linux/module.h>
#include <linux/kernel.h> // for printk
#include <linux/sched.h>  // for jiffies

// insmod 時に呼ばれる
int init_module()
{
  printk("module being installed at %lu\n", jiffies);
  return 0; // 0 以外の値を返すと、insmod は成功しない
}

// rmmod 時に呼ばれる
void cleanup_module()
{
  printk("module being removed at %lu\n", jiffies);
}

■Makefile
KERNEL_SRCDIR = /usr/src/linux
BUILD_DIR = $(shell pwd)

TARGET = moduletest

obj-m := $(TARGET).o

all:
        make -C $(KERNEL_SRCDIR) M=$(BUILD_DIR) modules

■注1
カーネルのシンボルを使うので、カーネルのヘッダファイルが必要。
apt-get install linux-headers などを行う。

■注2
init_module, cleanup_module という名前を変えるためには、
module_init, module_exit などのマクロを使う。


static int __init modtest_module_init( void )
{
  printk( KERN_INFO "modtest is loaded\n" );
  return 0;
}


static void __exit modtest_module_exit( void )
{
  printk( KERN_INFO "modtest is removed\n" );
}

module_init( modtest_module_init );
module_exit( modtest_module_exit );

■注3
デバッグ出力したいときは、printk を使う。出力は dmesg で確認する。

■注4
モジュールが insmod できたかどうかは lsmod で確認する。