1. Qiita
  2. 投稿
  3. Network

Amazon Dash ButtonでRouter Crash Buttonを作ってみた with Ruby

  • 17
    いいね
  • 0
    コメント

この記事は、NetOpsCoding Advent Calendar 2016の2016年12月17日の記事です。

Amazon Dash Button」を入手したので、ルータ系で何かできないかと考えた結果、ルータをクラッシュさせる「Router Crash Button」を作ってみました。ボタンを押すと、ルータがクラッシュします。

仕組み

ボタンを押してからルータがクラッシュまで、下記の通りで実行します。

  1. 人がボタンを押す
  2. スクリプトがボタンが押されたことを検知(ARPパケット検知)
  3. スクリプトがルータにTelnetする
  4. スクリプトがルータの隠しコマンド(test crash )を発行し、Bus Errorを強制的に発生
  5. ルータがクラッシュする
  6. ルータが再起動する
  7. また、人がボタンを押す。。。。
  8. 2に戻る

Amazon Dash Button

Amazon Dash Buttonは、インターネットと通信するために、IPアドレスがDHCPから割り当てられます。IPアドレスが割り当てられる機器のため、ボタンごとに、MACアドレスが異なります。このMACアドレスを利用して、ボタンを識別します。今回は「消臭力」のAmazon Dash Buttonを入手しました。設定方法は参考文献を参照してください。
Amazon Dash Button.png

ボタンが押されたことの検知(ARPパケット検知)

Amazon Dash Buttonが押された場合、ARPパケットがブロードキャストされます。このARPパケットをパケットキャプチャすることで、ボタンが押されたことを検知します。
Amazon Dash Buttonは、電源OFFの状態で、ボタンが押されると電源ONし、DHCPでIPアドレスの割当が行われた後、IPアドレスの重複確認のために、ARP Requestパケットが送信されます。このARP Requestパケットの送信元MACアドレスを確認し、Amazon Dash ButtonのMACアドレスと比較して、合致していればボタンが押されたと判断します。

環境

無線LAN環境で事前にAmazon Dash Buttonの設定をします。設定方法は参考文献を参照してください。

  • Ubuntu Linux 16.04
  • libpcap0.8 - パケットキャプチャ用
  • Ruby 2.3.0
    • expect4r 0.0.11 - Telnetライブラリ
    • pcaprub 0.12.4 - パケットキャプチャライブラリ
    • pio 0.30.0 - パケットパースライブラリ

コード

コードはgithubにありますので参照してください。
https://github.com/kooshin/router-crash-button

スクリプトの配置は下記のとおりです。

.
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── README.md
├── crash_button.rb     - Ruby実行ファイル(root権限で)
└── lib
    └── dash_button.rb  - Amazon Dash Button用のライブラリ

crash_button.rb

Amazon Dash Buttonが押されたらルータをクラッシュさせるスクリプトです。

下記の通り、パラメータを設定します。デモコードのため、すべてスクリプトに埋め込んであります。

DASH_BUTTON_MAC_ADDR  = '88:71:e5:63:aa:d5' # ボタンのMACアドレス
NETDEV                = 'enp0s25'           # パケットキャプチャするインタフェース

ROUTER_HOST           = '192.168.88.5'      # クラッシュさせるルータのIPアドレス
ROUTER_USER           = 'cisco'             # Telnet時のユーザ名
ROUTER_PASSWORD       = 'cisco'             # Telnet時のパスワード
ROUTER_ENABLE         = 'cisco'             # enableパスワード

自作DashButtonクラス(lib/dash_button.rb)のインスタンスを作成します。引数はパケットキャプチャするインタフェースと、検知するAmazon Dash ButtonのMACアドレスです。MACアドレスは、Wireshark等でパケットキャプチャすることでわかります。

dash_button = DashButton.new(NETDEV, DASH_BUTTON_MAC_ADDR)

DashButtonクラスのmonitorメソッドにブロックを渡します。Amazon Dash Buttonを検知するとブロックが実行されます。
今回は、ルータをクラッシュさせるコードを実行します。expect4rライブラリを使ってTelnetしています。詳細はRubyのexpect4rでCiscoルータにTelnet/SSHしてコマンド実行するを参照してください。
ルータにログイン後、ios.exp_printでtest crashコマンドを送信します。「Enable crash router selection marked with (crash router)」するために「C」を送信し、「(crash router) Bus Error, due to invalid address access」を引き起こすために、1を送信します。

dash_button.monitor do
  begin
    puts "#{DateTime.now} Crash Button pressed!! Force crash the Router"
    ios = Expect4r::Ios.new_telnet(
      host: ROUTER_HOST,
      user: ROUTER_USER,
      pwd:  ROUTER_PASSWORD,
      enable_password: ROUTER_ENABLE
    )
    ios.login
    ios.exp_print("test crash\rC\r1\r")
  rescue Expect4r::ExpTimeoutError, Errno::EIO => e
    puts "#{DateTime.now} Failed: #{e}"
  end
end

lib/dash_button.rb

pcaprubライブラリとpioライブラリを使って、Amazon Dash ButtonのARP Requestパケットを検出します。

PCAPRUBY::Pcap.open_liveでパケットキャプチャを開始します。この際、インタフェースの指定と、BPFフィルタでARPパケットのみパケットキャプチャするよう指定します。

  def pcap
    return @pcap if @pcap

    @pcap = PCAPRUB::Pcap.open_live(
      netdev,
      SNAPLENGTH,
      PROMISCOUS_MODE,
      TIMEOUT
    )
    @pcap.setfilter(BPF_FILTER)
  end

monitorメソッド内のpcap.each_packetでパケットを受信するたびに、ボタンが押されたかを判断します。パケットをPio::Parser.readでパースします。パースしたパケットを、dash_button_pressed?メソッドで、ボタンが押されたかどうかを判断します。ボタンが押されたと判断した場合、monitorメソッドの呼び出し元のブロックが実行されます。

  def monitor
    pcap.each_packet do |raw_packet|
      begin
        packet = Pio::Parser.read(raw_packet.data)
        yield if dash_button_pressed?(packet)
      rescue Pio::ParseError
        next
      end
    end
  end

dash_button_pressed?メソッド内では、ARP Requestパケットであるか、指定のMACアドレスであるかを判断します。

  def dash_button_pressed?(packet)
    arp_packet?(packet) && dash_button_mac_addr?(packet)
  end

  def arp_packet?(packet)
    Pio::Arp::Request == packet.class
  end

  def dash_button_mac_addr?(packet)
    dash_button_mac_addr == packet.source_mac
  end

実行

パケットキャプチャのために、libpcap-devを予めインストールし、コードをcloneして、bundleコマンドでRubyのライブラリをインストールします。

$ sudo apt install libpcap-dev
$ git clone https://github.com/kooshin/router-crash-button
$ bundle

パケットキャプチャするため、スクリプトの実行にはroot権限が必要です。sudoで実行すると良いでしょう。説明は省略していますが、Rubyはrbenvを用いてインストールしています。rbenvの場合、別途rbenv-sudoプラグインが必要です。

$ sudo ruby crash_button.rb
or
$ rbenv sudo ruby crash_button.rb
Waiting for Crash Button press...

実行結果

上記のTwitterの動画を参照してください。

参考文献