トラフィック量監視制御

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

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

L2スイッチの対応機種は、SWX2300-8GSWX2300-16GSWX2300-24GSWX2200-8GSWX2200-24G
SWX2200-8PoESWX2100-8GSWX2100-16Gです。

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

図 トラフィック量監視制御1
図 トラフィック量監視制御2
図 トラフィック量監視制御3

SWX2200のポートを定期的に監視し、パケットの受信負荷率が閾値を超えた場合には、ポートを遮断してトラフィック量を制御します。
SWX2200-24Gを使用している場合は、ポートを遮断する前に受信帯域を絞り、トラフィック量を抑制します。それでもなお受信負荷率が閾値を超える場合にはポートを遮断します。

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

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

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

RTX1210 RTX1200 RTX810 NVR700W NVR510 NVR500

コマンド設定例
Luaスクリプト例

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

L2スイッチ

SWX2200-8G SWX2200-24G SWX2200-8PoE

L2MSスレーブ機能

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_traffic_ctl_rtx1200.lua

設定値

-- 監視間隔(1 - 864000 秒)
idle_time = 60
-- 受信負荷率を計測する時間(1, 2 .. )
avr_sec = 1
-- 受信負荷率の閾値(1 - 99 %)
rcv_th = 30
-- [[
連続して閾値を超えたら受信帯域を絞る回数(1, 2, ..)
SWX2200-24G のみ有効。down_countよりも少ない回数を指定する必要がある
]]
shaping_cnt = 3
-- 連続して閾値を超えたらポートを閉じる回数(1, 2, ..)
down_cnt = 5
--[[
帯域を絞る場合のスピードレベル(1 - 31)
switch control function set qos-speed-unit コマンドの設定値 (初期値 32M) に
shaping_speed を掛けた値が実際の帯域幅となる
SWX2200-24Gのみ有効
]]
shaping_speed = 10
-- このスクリプトが出力する SYSLOG のレベル (info, debug, notice)
log_level = "info"
--[[
監視対象のスイッチと監視するポートをカンマで区切って列挙する
MACアドレスかルーターからスイッチまでの接続ポートの経路情報で指定
("00:a0:de:xx:xx:xx" または "lan1:2")
監視するポートを列挙する(port = "1, 3-5, 7")
]]
sw_list = {
    { route = "lan1:1", port = "2-8"}
}
-- (送る: true / 送らない: false)
mail = false
-- メールの設定
mail_tbl = {
    smtp_address = "(SMTPサーバのアドレス)",
    from = "(送信元メールアドレス)",
    to = "(宛先メールアドレス)"
}

ポートの通信速度を求める関数

function port_speed(num, route)
    local rtn, str, val, rt_name
    local cmd = "switch control function get status-port-speed " .. num
    local ptn = "(%d+)"

    rt.command("switch select " .. route)
    rtn, str = rt.command(cmd)
    val = 0
    if (rtn) and (str) then
        str = str:match(ptn)
        if (str) then
            val = unitstr2num(str)
        end
    end
    return val
end

回線速度を数値に変換する関数

function unitstr2num(str)
    local val

    val = tonumber(str)
    if (val > 0) then
        val = val * 1024 * 1024
    end
    return val
end

受信オクテット数を求める関数

function port_rx_octet(num, route)
    local rtn, str
    local cmd = "switch control function get status-counter-octet-rx " .. num
    local ptn = "(%d+)"

    rt.command("switch select " .. route)
    rtn, str = rt.command(cmd)
    if (rtn) and (str) then
        str = string.match(str, ptn)
    end
    return tonumber(str)
end

オクテット数の差分と回線速度から受信負荷率を求める

function calc_load(port)
    local load, diff

    if (port.speed == 0) then
        return 0
    end

    if (port["octet1"] > port["octet2"]) then
        -- カウンタの上限を超えた場合
        diff = port.speed - port.octet1 * 8
        load = (diff + port["octet2"] * 8) / avr_sec
    else
        diff = port["octet2"] - port["octet1"]
        load = diff * 8 / avr_sec -- 速度(bps)
    end
    load = 100 * load / port.speed -- 負荷率
    return load
end

回線使用率が閾値を超えた時、または正常に復帰した時のメッセージを返す関数

function make_portmsg(port, route, load, stat)
    local rtn
    local str = ""

    if (stat == "shaping") then
        mail_tbl.text = string.format("受信負荷率が閾値を超えたため、"..
                       "switch(%s)のポート%dの帯域を絞りました。"..
                       "\r\n\r\n", route, port)
        mail_tbl.text = mail_tbl.text .. "\t受信帯域のレベルを" .. shaping_speed ..
                "に変更しました。\r\n"
        mail_tbl.text = mail_tbl.text .. "\t閾値: " .. rcv_th .. "%"
    elseif (stat == "down") then
        mail_tbl.text = string.format("受信負荷率が閾値を超えたため、"..
                       "switch(%s)のポート%dを閉じました。\r\n\r\n",
                       route, port)
        mail_tbl.text = mail_tbl.text .. "\t受信負荷率:" .. load .. "%\r\n"
        mail_tbl.text = mail_tbl.text .. "\t閾値:" .. rcv_th .. "%"
    end
end

閾値を超えた連続回数をカウントする関数

function count_proc(t, val, port_num)
    local rtn = 0

    if (val > rcv_th) then
        -- 閾値を超えた場合
        t.cnt = t.cnt + 1
        -- 24G限定
        if (port_num == 24) then
            if (t.stat == "normal") and (t.cnt == shaping_cnt) then
                t.stat = "shaping"
                rtn = 1
            end
        end

        if (t.stat ~= "down") and (t.cnt == down_cnt) then
            t.stat = "down"
            rtn = 1
        end
    else
        -- 受信負荷率が閾値以下の場合、カウンタをリセットする
        t.cnt = 0
    end
    return rtn, t
end

ポートの受信帯域/ポートをダウンさせる関数

function exec_sw_cmd(route, stat, port)
    rtn, str = rt.command("switch select " .. route)

    if (rtn) then
        if (stat == "shaping") then
            -- ポートを絞る
            rt.command("switch control function set qos-policing-use "
                   .. port .. " on")
            rt.command("switch control function set qos-policing-speed "
                    .. port .. " " .. shaping_speed)
            -- syslogを出力
            msg = string.format("over the receive threshold. "..
                        "port%d speed down", port)
            rt.syslog(log_level, msg)
        elseif (stat == "down") then
            rt.command("switch control function set port-use ".. port .. " off")
            -- syslogを出力
            msg = string.format("over the receive threshold. port%d down",
                         port)
            rt.syslog(log_level, msg)
        end
    end
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 send_mail(port, route, load, stat)
    local rtn

    make_portmsg(port, route, load, stat)
    mail_tbl.subject = string.format("receive load watch")
    rtn = rt.mail(mail_tbl)
    if (not rtn) then
        rt.syslog(log_level, "failed to send mail. swx2200_lua_traffic_ctl_rtx1200.lua")
    end
end

SWの検索および機種、ポート数の設定

function set_sw_tbl(tbl)
    for i, sw in ipairs(sw_list) do
        route = sw.route
        rtn, str = rt.command("switch control function get model-name ".. route)
        if (rtn) and (str) then
            str = string.match(str, "%w+%p(%d+).")
            if (str) then
                tbl[i] = {
                    route = route,
                    port_num = tonumber(str),
                    port_info = {},
                    check_port = {}
                }
            else
                stbl[i] = nil
            end
        else
            tbl[i] = nil
        end
    end
end

SWの各ポート情報を初期化

function port_info_init(tbl, sw_num)
    local sw

    for i=1, sw_num do
        sw = tbl[i]
        if (sw ~= nil) then
            for j=1, sw.port_num do
                sw.port_info[j] = {
                    speed = 0, octet1 = 0, octet2 = 0, result = 0, cnt = 0, stat = "normal"
                }
            end
        end
    end
end

SWの監視するポートを設定

function set_check_port(tbl, sw_num)
    local sw, port
    local min, max, port_val

        for i=1, sw_num do
        sw = tbl[i]
        if (sw ~= nil) then
            port = sw_list[i].port
            j = 1
            str = ""
            while (true) do
                port_val = string.match(port, str.."(.-),")
                if (port_val == nil) then
                    port_val = string.match(port, str .."(.-)$")
                    if (port_val == nil) then
                        break
                    end
                end
                --要素が見つかった場合
                min, max = string.match(port_val,"(%d+)%p(%d+)")
                if (min ~= nil) and (max ~= nil) then
                    --範囲指定
                    min = tonumber(min)
                    max = tonumber(max)
                    for n = min, max do
                        sw.check_port[j] = n
                        j = j + 1
                        n = n + 1
                    end
                else
                    --単体指定
                    sw.check_port[j] = tonumber(port_val)
                    j = j + 1
                end
                str = str .. ".-,"
            end
        end
    end
end

SWの各ポートのオクテット数の取得

function get_port_info(sw, port_num, str)
    local port
    --監視するポートの場合
    port = sw.port_info[port_num]
    if (str == "octet1") then
        -- 受信オクテット数を求める(1回目)
        octet = port_rx_octet(port_num, sw.route)
        -- オクテット数を保存
        port["octet1"] = octet
    elseif (str == "octet2") then
        -- 受信オクテット数を求める(2回目)
        octet = port_rx_octet(port_num, sw.route)
        -- オクテット数を保存
        port["octet2"] = octet
    end
end

メインルーチン

local rtn, str, max
local sw, port
local port_info = {}
local sw_tbl = {}

-- 指定されたSWXを検索し、製品名を判別する。
set_sw_tbl(sw_tbl)
--SWの各ポート情報用配列の初期化
port_info_init(sw_tbl, #sw_tbl)
--SWの監視するポート番号をセット
set_check_port(sw_tbl, #sw_tbl)

while (true) do
    for i = 1, #sw_tbl do
        sw = sw_tbl[i]
        num = 1
        check = sw.check_port[num]

        for j = 1, sw.port_num do
            if (j == check) then
                port = sw.port_info[j]
                max = port_speed(port_num, sw.route)
                port["speed"] = max
                if (max > 0) then
                    --1回目
                    get_port_info(sw, j, "octet1")
                    --計測時間待ち
                    rt.sleep(avr_sec)
                    --2回目
                    get_port_info(sw, j, "octet2")
                    --受信負荷率を求める
                    load = calc_load(port)
                    --閾値を超えた回数のカウント
                    rtn, port = count_proc(port, load, sw.port_num)

                    if (rtn > 0) then
                        --[[
                        ポートの受信帯域を絞る/ポートのシャットダウン
                        ]]
                        exec_sw_cmd(sw.route, port.stat, j)
                        -- メール送信
                        if (mail) then
                            send_mail(j, sw.route, load, port.stat)
                        end
                    end
                end
                num = num + 1
                check = sw.check_port[num]

                if (check == nil) then
                    break
                end
            end
        end
    end
    -- 測定間隔待ち
    rt.sleep(idle_time)
end

【ご注意】

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

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