ブロードキャストストームの抑制

本設定例では、ルーターのL2MSコントローラー機能・Luaスクリプト機能と、L2スイッチのL2MSスレーブ機能を使用しています。L2MSを使って、ルーターから、スイッチが持つ機能の設定を行います。

ルーターの対応機種は、RTX1210RTX1200(Open in new windowRev.10.01.16以降)、RTX810NVR700WNVR510NVR500
FWX120です。

L2スイッチの対応機種は、SWX2200-8GSWX2200-24GSWX2200-8PoEです。

L2MSコントローラー(ルーター)とL2MSスレーブ(L2スイッチ)の対応リビジョンは、Open in new window 技術資料「L2MS」でご確認ください。

図 Luaスクリプト構成図1
図 Luaスクリプト構成図2

SWX2200のポートを監視し、ブロードキャストストームの発生を検知した際、受信量の最も多いSWX2200のポートのブロードキャストパケットに関する受信帯域を絞り、ブロードキャストストームを抑制します。
また、本設定例はマルチキャストパケットについても同様の処理を行い、マルチキャストストームを抑制することができます。

光回線に接続するためには、別途ONUが必要です。
NVR700WとNVR510は、本体のONUポートに小型ONUを装着することで、光回線に接続できます。

本設定例は、Luaスクリプト例とブロードキャストパケットおよびマルチキャストパケットの抑制を行うためのモジュールファイルを使用します。このモジュールファイルをルーターにコピーする必要があります。

設定手順

本設定例は、以下の流れで設定します。

  1. ルーターにConfigを設定
  2. 構成を構築
  3. ルーターにモジュールファイルをコピー
  4. Luaスクリプトを実行

対応機種のうち、設定例を掲載している機種は、以下のとおりです。

機種 掲載内容 備考
ルーター

RTX1210 RTX1200 RTX810 NVR700W NVR510

コマンド設定例
Luaスクリプト例
モジュールファイル

L2MSコントローラー機能、
Luaスクリプト機能

L2スイッチ

SWX2200-8G SWX2200-24G

L2MSスレーブ機能

モジュールファイルを利用した設定の導入手順

■ルーターに本設定例のconfigを設定する

  1. 設定例のリンクから設定ファイルをダウンロードし、ルーターに設定を追加してください。

pp auth myname (ISPに接続するID) (ISPに接続するパスワード)
dns server (ISPより指定されたDNSサーバのIPアドレス)

[解説]
上記のように設定例で黄色で記述されている設定値は、適切な値に変更してください。

ルーターにはTFTPを使用してPCからルーターに設定ファイルをコピーすることができます。
この方法については取扱説明書をご参照ください。

■構成を構築する

  1. 本設定例ページのトップにある通常時の構成図と同様の構成を構築します。

■モジュールファイルをルーターにコピーする

  1. モジュールファイルをPCにダウンロードします。

    [解説]
    モジュールファイル名はLuaスクリプトの動作にも関係がありますので、本設定例では変更せずそのままご使用ください。

  2. PCにUSBメモリを接続して、ダウンロードしたモジュールファイルをUSBメモリにコピーします。
  3. USBメモリをPCから取り外し、ルーターに接続します。ルーターのUSBランプが点灯します。
  4. ルーターコンソールを開きます。

    [解説]
    ルーターコンソールはシリアルケーブルやtelnetで接続して使用します。
    使用方法については取扱説明書をご参照ください。

  5. 管理者権限でログインし、コマンドでルーターにモジュールファイルをコピーします。
    copyコマンドを使用します。
    USBメモリのルートディレクトリからルーターのルートディレクトリにモジュールファイルをコピーする場合:
    [ルーターコンソール]

    # copy usb1:/lua_rtx1200_storm-limit.lib /lua_rtx1200_storm-limit.lib
    #

    [解説]
    copyコマンドの詳細はこちらをご参照ください。
    ファイルをルーターのルートディレクトリ以外にも格納することができます。
    その場合は、ディレクトリを作成してから、そのディレクトリにコピーします。
    そちらの方法については参考資料をご参照ください。
    技術資料「RTFS」-「コマンド一覧」-「ディレクトリの作成」
    モジュールファイルをディレクトリにコピーした場合、環境変数LUA_PATHでそのディレクトリを指定する必要があります。そちらの方法は参考資料をご参照ください。
    技術資料「Lua スクリプト機能」 - 「詳細」にある「5.ルーターの環境変数 」をご参照ください。
    モジュールファイルの名前を変更した場合、Luaスクリプト内のrequire関数の引数の名前も変更する必要があります。

    [補足]
    本例のモジュールファイルをcopyコマンドではなくTFTPを使用してRTFSへコピーする場合はバイナリモードでコピーしてください。

■Luaスクリプトの導入手順

[解説]
Luaスクリプトのダウンロードから実行までの手順はLuaスクリプト導入手順マニュアルをご覧ください。

環境変数の設定

set LUA_PATH="./\?.lib;" #注釈1

LANの
インタフェースの設定
(LAN1ポートを使用)

ip lan1 address 192.168.0.1/24

WANの
インタフェースの設定
(LAN2ポートを使用)

pp select 1
pp always-on on
pppoe use lan2
pp auth accept pap chap
pp auth myname (ISPに接続するID) (ISPに接続するパスワード)
ppp lcp mru on 1454
ppp ipcp ipaddress on
ppp ipcp msext on
ip pp mtu 1454
ip pp nat descriptor 1
pp enable 1
ip route default gateway pp 1

NATの設定

nat descriptor type 1 masquerade

DHCPの設定

dhcp service server
dhcp server rfc2131 compliant except remain-silent
dhcp scope 1 192.168.0.2-192.168.0.100/24

DNSの設定

dns server (ISPより指定されたDNSサーバのIPアドレス)
dns private address spoof on

SWX2200の設定

switch control use lan1 on

フィルタの設定

ip filter source-route on
ip filter directed-broadcast on
ip filter 1010 reject * * udp,tcp 135 *
ip filter 1011 reject * * udp,tcp * 135
ip filter 1012 reject * * udp,tcp netbios_ns-netbios_ssn *
ip filter 1013 reject * * udp,tcp * netbios_ns-netbios_ssn
ip filter 1014 reject * * udp,tcp 445 *
ip filter 1015 reject * * udp,tcp * 445
ip filter 1020 reject 192.168.0.0/24 *
ip filter 1030 pass * 192.168.0.0/24 icmp
ip filter 2000 reject * *
ip filter 3000 pass * *
ip filter dynamic 100 * * ftp
ip filter dynamic 101 * * www
ip filter dynamic 102 * * domain
ip filter dynamic 103 * * smtp
ip filter dynamic 104 * * pop3
ip filter dynamic 105 * * netmeeting
ip filter dynamic 106 * * tcp
ip filter dynamic 107 * * udp
pp select 1
ip pp secure filter in 1020 1030 2000
ip pp secure filter out 1010 1011 1012 1013 1014 1015 3000 dynamic 100 101 102 103 104 105 106 107
pp enable 1

Luaスクリプトのスケジュール設定

schedule at 1 startup * lua /swx2200_lua_bc_storm_ctl_rtx1200.lua

設定値

-- 転送レート上限
broadcast_rate = 5
multicast_rate = 5
-- 監視対象のスイッチをカンマで区切って列挙する
-- MACアドレスかルーターからスイッチまでの接続ポートの経路情報で指定
-- ("00:a0:de:xx:xx:xx" または "lan1:2-3")
sw_list = {
    "lan1:1"
}
-- パケットストームの監視間隔(1 - 864000 秒)
idle_time = 60
-- パケットストーム連続検出閾値 (1 - 864000)
-- 推奨値は 3
continue_time = 3
-- ブロードキャストストーム検出時のメール送信設定
-- (送る: true / 送らない: false)
mail = false
-- メールの設定
mail_tbl = {
    smtp_address = "(SMTP サーバのアドレス)",
    from = "(送信元メールアドレス)",
    to = "(宛先メールアドレス)"
}
-- スイッチの状態、メール送信失敗等の情報を出力する SYSLOG のレベル (info, debug, notice)
log_level = "info"

グローバル変数の設定

-- 転送制限が連続した回数
limit_cnt = {0, 0, 0, 0, 0, 0, 0, 0}
-- 転送制限設定とカウンタ設定の状態
limit_cfgd = {nil, nil, nil, nil, nil, nil, nil, nil}
-- 転送制限が始まった時のカウンタ値
cnt_save = {nil, nil, nil, nil, nil, nil, nil, nil}

ロードするモジュールの指定

require("lua_rtx1200_storm-limit")

スイッチの機種を取得する関数

function switch_model_read(sw)
    local rtn, str
    local cmd = "switch control function get model-name " .. sw

    rtn, str = rt.command(cmd)
    if (not rtn) or (not str) then

        str = string.format("failed to get model name : %s\r\n", sw)
    end
    return rtn, str
end

スイッチの名前を取得する関数

function switch_sys_name_read(sw)
    local rtn, str
    local cmd = "switch control function get system-name " .. sw

    rtn, str = rt.command(cmd)
    if (not rtn) or (not str) then
        str = string.format("failed to get switch system name : %s\r\n", sw)
    end
    return rtn, str
end

スイッチのカウンタタイプを設定する関数

function switch_set_counter(port, cnt_num)
    local cmd = string.format("switch control function set "..
                     "counter-frame-rx-type %d %d "..
                     "broadcast-and-multicast-packets", port, cnt_num)

    return rt.command(cmd)
end

スイッチのカウンタを読みだす関数

function switch_read_frame_counter(sw, rx_tx, port, cnt_num)
    local rtn, val
    local cmd = string.format("switch control function get "..
                     "status-counter-frame-%s %d %d %s",
                     rx_tx, port, cnt_num, sw)

    rtn, val = rt.command(cmd)
    if (not rtn) or (not val) then
        return nil
    end
    return rtn, tonumber(val)
end

受信カウンタタイプをbroadcast-and-multicast-packetsに設定する関数

function set_counter_watch_pkts(sw)
    local rtn, str, port_num, cnt_id
    local cmd = "switch select " .. sw

    rtn = rt.command(cmd)
    if (not rtn) then
        return nil
    end

    rtn, str = switch_model_read(sw)
    if (not rtn) or (not str) then
        return nil
    end

    if (string.find(str, "SWX2200-8G", 1, true)) then
        port_num = 8
        cnt_id = 3
    elseif (string.find(str, "SWX2200-24G", 1, true)) then
        port_num = 24
        cnt_id = 5
    else
        return nil
    end

    for i = 1, port_num do
        rtn = switch_set_counter(i, cnt_id, "broadcast-and-multicast-packets")
        if (not rtn) then
            return nil
        end
    end

    cmd = "switch select none"
    rtn = rt.command(cmd)
    return rtn
end

受信した抑制対象フレーム数を取得する関数

function read_counter_watch_pkts(sw)
    local rtn, str, port_num, cnt_id
    local val = {}

    rtn, str = switch_model_read(sw)

    if (not rtn) or (not str) then
        return nil
    end

    if (string.find(str, "SWX2200-8G", 1, true)) then
        port_num = 8
        cnt_id = 3
    elseif (string.find(str, "SWX2200-24G", 1, true)) then
        port_num = 24
        cnt_id = 5
    else
        return nil
    end

    for i = 1, port_num do
        rtn, val[i] = switch_read_frame_counter(sw, "rx", i, cnt_id)
        if (not rtn) or (not val[i]) then
            return nil
        end
    end
    return rtn, val
end

現在の日時を取得する関数

function time_stamp()
    local t

    t = os.date("*t")
    return string.format("%d/%02d/%02d %02d:%02d:%02d",
        t.year, t.month, t.day, t.hour, t.min, t.sec)
end

カウンタの増加量がもっとも大きいポートを取得する関数

function get_port_cntinc_max(curr, prev)
    local maxport
    local maxinc = 0

    if (curr == nil) or (prev == nil) then
        return nil
    end
    if (#curr ~= #prev) then
        return nil
    end
    for i, c in ipairs(curr) do
        inc = curr[i] - prev[i]
        if (inc > maxinc) then
            maxinc = inc
            maxport = i
        end
    end
    return maxport
end

パケットストームの状態検出を行う関数

function get_storm_status(idx, sw)
    local rtn, str, storm, limit

    -- SWX2200の転送制限フラグを確認する
    rtn = swx2200.is_storm_stt(sw)
    if (rtn == true) then
        limit = true
        limit_cnt[idx] = limit_cnt[idx] + 1
        -- SWX2200の転送制限フラグをクリアする
        rtn = swx2200.clear_storm_stt(sw)
        if (limit_cnt[idx] >= continue_time) then
            storm = true
        end
    elseif (rtn == false) then
        limit = false
        limit_cnt[idx] = 0
    else
        if (limit_cnt[idx] > 0) then
            limit = true
        else
            limit = false
        end
        str = string.format("failed to check packet storm status : "..
                    "'%s' swx2200_lua_bc_storm_ctl_rtx1200.lua", sw)
        rt.syslog(log_level, str)
    end
    return storm, limit
end

スイッチ毎に状態を監視を行う関数

function watch_switch(idx, sw)
    local rtn, str, cnt_curr, msg, m_port, storm, limit

    if (not limit_cfgd[idx]) then
        -- SWX2200のパケットストーム制限・検出を有効にする
        limit_cfgd[idx] = swx2200.set_storm_limit(sw, broadcast_rate,
                                   multicast_rate)
        rtn = set_counter_watch_pkts(sw)
        limit_cfgd[idx] = limit_cfgd[idx] and rtn
        if (not limit_cfgd[idx]) then
            str = string.format("failed to set packet storm limit : "..
                        "'%s' swx2200_lua_bc_storm_ctl_rtx1200.lua", sw)
            rt.syslog(log_level, str)
        end
    end

    if (limit_cfgd[idx]) then
        storm, limit = get_storm_status(idx, sw)
    end

    if (limit) then
        rtn, cnt_curr = read_counter_watch_pkts(sw)
        if (rtn) then
            if (cnt_save[idx] == nil) then
                cnt_save[idx] = {}
                for p, c in ipairs(cnt_curr) do
                    cnt_save[idx][p] = c
                end
            end
            m_port = get_port_cntinc_max(cnt_curr, cnt_save[idx])
        end
        str = string.format("broadcast-and-multicast-packets forwarding "..
                    "is limited : %s swx2200_lua_bc_storm_ctl_rtx1200.lua",
                     sw)
        rt.syslog(log_level, str)
    else
        cnt_save[idx] = nil
    end

    if (storm) then
        rtn, str = switch_sys_name_read(sw)
        str = string.split(str, "\r\n")
        msg = string.format("* %s (%s)\r\n", str, sw)
        rtn, str = switch_model_read(sw)
        msg = msg .. string.format(" 機種名 : %s", str)
        str = string.format("packet storm detected : %s "..
                    "swx2200_lua_bc_storm_ctl_rtx1200.lua", sw)
        rt.syslog(log_level, str)
        if (m_port ~= nil) then
            msg = msg .. string.format(" (ポート %d で大量の"..
                        "パケットを受信しました)\r\n",
                         m_port)
            str = string.format("large amounts of broadcast-and-"..
                        "multicast-packets received : "..
                        "%s port %d swx2200_lua_bc_storm_ctl_rtx1200.lua",
                         sw, m_port)
            rt.syslog(log_level, str)
        end
        msg = msg .. "\r\n"
    end
    return storm, limit, msg
end

メインルーチン

local rtn, rtn2, str, storm, limit, msg
if (_RT_LUA_VERSION_NUM < 101) then
    str = "Lua script function version 1.01 or higher is required, terminated. "..
        "swx2200_lua_bc_storm_ctl_rtx1200.lua"
    rt.syslog(log_level, str)
    return
end

while (true) do
    storm = false
    limit = false
    msg = ""
    for i, sw in ipairs(sw_list) do
        rtn, rtn2, str = watch_switch(i, sw)
        if (rtn) and (str) then
            storm = true
            msg = msg .. str
        end
        if (rtn2) then
            limit = true
        end
    end

    if (storm) and (mail) then
        mail_tbl.text = "以下のスイッチでパケットストームを検出しました"..
                "\r\n\r\n" .. msg
        mail_tbl.subject = string.format("packet storm notify (%s)",
                            time_stamp())
        rtn = rt.mail(mail_tbl)
        if (rtn) then
            str = "send packet storm notify mail. " ..
                "swx2200_lua_bc_storm_ctl_rtx1200.lua"
            rt.syslog(log_level, str)
        else
            str = "failed to send mail. swx2200_lua_bc_storm_ctl_rtx1200.lua"
            rt.syslog(log_level, str)
        end
    end

    if (limit) and (not storm) then
        rt.sleep(1)
    else
        rt.sleep(idle_time)
    end
end

[注釈の説明]
注釈1:
モジュールファイルをロードするために使用するパスを設定します。
Lua ではモジュール名の置換記号として "?" を使いますが、ルーターコンソールで '?' を入力するとヘルプが表示されてしまうため、'?' を入力する前にエスケープシーケンスの '\' を入力しています。

require関数の引数と環境変数LUA PATHの設定値についての詳細はこちら

【ご注意】

本設定例は、設定の参考例を示したもので、動作を保証するものではございません。
ご利用いただく際には、十分に評価・検証を実施してください。

ページトップへ戻るReturn to Top