LoginSignup
21
20

More than 5 years have passed since last update.

CiscoルータのルーティングテーブルをRuby/Sinatraで見える化して検証捗らせてみた

Posted at

はじめに

この記事はNetOpsCoding Advent Calendar 2015 の22日目の記事です。検証作業を捗らせるために一度に複数台のCiscoルータのルーティングテーブルを見える化するツールをRuby/Sinatra+SNMPで試作しました。
WebRoutes.png

目的

検証作業では、複数台のルータのルーティングテーブルを都度確認する必要があります。ネットワークエンジニアなら各ルータにTELNET/SSHしてshow ip routeコマンドを打つのは手馴れていると思います。

ただ、ルータの台数が多くなると結構面倒です。マクロなどでshow ip routeを自動実行してもいいですが、CLIではルーティングテーブルを確認するのは大変です。GUIでルーティングテーブルを確認しようと考えた結果、WEBブラウザでルーティングテーブルを見える化するツールを試作しました。

ルータのルーティングテーブルを正規化して表形式で表示することで見やすくします。実装にあたっては、show ip routeを正規表現で抽出するのは至難の業なので、SNMPを使って抽出することにしました。

概要

使用している技術はNetOpsCoding Advent Calendar 2015の14日目のCiscoルータのポートをRuby/Sinatraで見える化して検証捗らせてみたと同等となります。そのため、実行環境等はまったく同じです。

  • Cisco 1812J
  • Ubuntu 15.04
  • Ruby 2.2.3
  • snmp 1.2.0(RubyでSNMP)
  • sinatra 1.4.6(RubyのWebフレームワーク)
  • slim(RubyのHTMLテンプレートエンジン)
  • bootstrap3(HTML/CSSデザイン)

今回はグローバルルーティングテーブル(address-family ipv4)のルーティングテーブルを見える化します。ルーティング情報は標準MIBのIP-FORWARD-MIBのipCidrRouteEntryを用いています。

ipCidrRouteEntry
  '1.3.6.1.2.1.4.24.4.1.5', # IP-FORWARD-MIB::ipCidrRouteIfIndex
  '1.3.6.1.2.1.4.24.4.1.1', # IP-FORWARD-MIB::ipCidrRouteDest
  '1.3.6.1.2.1.4.24.4.1.2', # IP-FORWARD-MIB::ipCidrRouteMask
  '1.3.6.1.2.1.4.24.4.1.4', # IP-FORWARD-MIB::ipCidrRouteNextHop
  '1.3.6.1.2.1.4.24.4.1.6', # IP-FORWARD-MIB::ipCidrRouteType
  '1.3.6.1.2.1.4.24.4.1.8', # IP-FORWARD-MIB::ipCidrRouteAge

スクリプト

ルーティングテーブル見える化ツールは下記のファイルで構成されています。

ファイル構成
├── Gemfile         -- Ruby依存関係の記述ファイル
├── views           
│   └── index.slim  -- HTMLテンプレートファイル(slim形式)
└── webroutes.rb    -- 実行ファイル本体兼Webサーバ

GemfileはBundleでRubyGemsの依存関係を記述したファイルです。

Gemfile
source 'https://rubygems.org'
gem 'snmp'
gem 'sinatra'
gem 'sinatra-contrib'
gem 'slim'
gem 'parallel'
gem 'ipaddress'

webroutes.rbは実行ファイル本体です。設定箇所はHOSTS変数とCOMMUNITY変数です。HOSTS変数で対象ルータを指定しています。COMMUNITY変数でSNMPのコミュニティ名を指定しています。Rubyのparallelライブラリを使用して、in_threads: 4を指定することで、同時に4台のルータでSNMPの値を取得します。

webroutes.rb
require 'sinatra'
require 'sinatra/reloader'
require 'slim'
require 'snmp'
require 'parallel'
require 'ipaddress'

set :bind, '0.0.0.0'

HOSTS = %w(192.168.88.101 192.168.88.102)
COMMUNITY = 'public'
WALK_COLUMNS = [
  '1.3.6.1.2.1.4.24.4.1.5', # IP-FORWARD-MIB::ipCidrRouteIfIndex
  '1.3.6.1.2.1.4.24.4.1.1', # IP-FORWARD-MIB::ipCidrRouteDest
  '1.3.6.1.2.1.4.24.4.1.2', # IP-FORWARD-MIB::ipCidrRouteMask
  '1.3.6.1.2.1.4.24.4.1.4', # IP-FORWARD-MIB::ipCidrRouteNextHop
  '1.3.6.1.2.1.4.24.4.1.6', # IP-FORWARD-MIB::ipCidrRouteType
  '1.3.6.1.2.1.4.24.4.1.8', # IP-FORWARD-MIB::ipCidrRouteAge
]
Route = Struct.new(:ifindex, :dest, :mask, :nexthop, :type, :age, :prefix, :time, :iface)
Host = Struct.new(:name, :routes, :ifaces)

get '/' do
  @hosts = []
  Parallel.each(HOSTS, in_threads: 4) do |host|
    begin
      SNMP::Manager.open(host: host, community: COMMUNITY, timeout: 1, retries: 3) do |manager|
        ifaces = {}
        manager.walk('ifDescr') do |vb|
          index = vb.name.last.to_s
          value = vb.value.to_s
          ifaces[index] = value
        end

        routes = []
        manager.walk(WALK_COLUMNS) do |row|
          values = row.map { |vb| vb.value.to_s }
          route = Route.new(*values)
          route.prefix = IPAddress("#{route.dest}/#{route.mask}").to_string
          route.iface = ifaces[route.ifindex]
          route.time = Time.at(Time.now.to_i - route.age.to_i)
          routes << route
        end

        @hosts << Host.new(host, routes, ifaces)
      end
    rescue => e
      @hosts << Host.new("#{host} - #{e}", [], {})
    end
  end
  @hosts.sort_by!(&:name)

  slim :index
end

views/index.slimはWeb画面用のHTMLテンプレートファイルです。Bootstrap3を利用して、見た目をちょっとかっこよくしています。Boostrap3やjQueryはCDNのファイルを参照するようにしています。検証環境からインターネットに接続できない場合は、ローカルに各種ファイルをダウンロードしてpublicディレクトリ配下に入れる必要があります。

views/index.slim
doctype html
html lang="ja"
  head
    meta charset="utf8"
    meta http-equiv="X-UA-Compatible" content="IE=edge"
    meta name="viewport" content="width=device-width, initial-scale=1"
    link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" crossorigin="anonymous"
    title WebRoutes
  body
    - @hosts.each do |host|
      h3
        = host.name
        small
          | &nbsp;Global Routing Table
      table.table.table-condensed.table-striped.table-hover
        thead
          th #
          th Prefix
          th Dest
          th Mask
          th Nexthop
          th Port
          th Since
        tbody
          - host.routes.each_with_index do |route, index|
            tr
              td = index + 1
              td = route.prefix
              td = route.dest
              td = route.mask
              td = route.nexthop
              td = route.iface
              td = route.time
    script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"
    script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" crossorigin="anonymous"

実行結果

実行結果
$ bundle
Using backports 3.6.7
Using ipaddress 0.8.0
Using multi_json 1.11.2
Using parallel 1.6.1
Using rack 1.6.4
Using rack-protection 1.5.3
Using rack-test 0.6.3
Using tilt 2.0.1
Using sinatra 1.4.6
Using sinatra-contrib 1.4.6
Using temple 0.7.6
Using slim 3.0.6
Using snmp 1.2.0
Using bundler 1.10.6
Bundle complete! 6 Gemfile dependencies, 14 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.
$ ruby webroutes.rb
[2015-12-06 00:08:35] INFO  WEBrick 1.3.1
[2015-12-06 00:08:35] INFO  ruby 2.2.3 (2015-08-18) [x86_64-linux]
== Sinatra (v1.4.6) has taken the stage on 4567 for development with backup from WEBrick
[2015-12-06 00:08:35] INFO  WEBrick::HTTPServer#start: pid=30233 port=4567

Webブラウザからhttp://localhost:4567にアクセスすると、経路の状態を一括して確認できます。
画面を更新すると最新の状態を確認できます。
WebRoutes.png

表の見方

  • Prefix : 経路をプレフィックス形式で表しています。
  • Dest : 経路のネットワークアドレスを表しています。
  • Mask : 経路のサブネットマスクを表しています。
  • Nexthop : 経路のネクストホップアドレスを表しています。0.0.0.0の場合はは自分自身を表します。
  • Port : 経路のネクストホップのポートを表しています。
  • Since : 経路を学習した時刻を表しています。

工夫点

  1. 経路学習のタイミングもルータ内で保持していますが、現在時刻から何秒前など見難いため、 実際の学習時刻に変換して見やすくしています。SNMPの取得タイミングによって1秒程度誤差がでます。
  2. 見やすいプレフィックス形式で表示しています。
  3. 表形式で表示しているため、show ip routeよりも若干見やすくなっています。
  4. 一度に複数のルータでSNMP取得するため、Parallelを用いて多重化しています。

おわりに

今回は、グローバルルーティングテーブルを表示させる機能を試作しています。実際の検証で作成したものはBGPのVPNv4を表示させるツールでしたが、あまり一般的ではないと思い作りなおし、必要最低限の機能のみ実装しています。

参考

21
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
20