广播风暴的抑制

监视SWX2200的端口、当检测出发生了广播风暴时、缩小与接收量最大的SWX2200端口的广播报文相关的接收带宽、抑制广播风暴。
并且、本设定例对多播数据包也可采取同样的处理、以抑制多播风暴。

本设定例中、Lua脚本和进行抑制广播风暴(或多播风暴)需要使用模块文档。需要将这个模块文档拷贝到路由器中。

利用模块文件的设定导入程序

在RTX1200设定本设定例的config

请从设定例的链接上下载设定文件,追加设定到RTX1200

pp auth myname(连接ISP的ID) (连接ISP的密码)
dns server(由ISP指定的DNS服务器的IP地址)

[说明]
如上所述,在设定例中用黄色表示的设定值,请变更为适当的值。

在RTX1200中,可使用TFTP从PC向路由器拷贝设定文件。
关于此种方法请参照使用说明书。

构建结构

构建与位于本设定例页面起始部位的平时结构图相同的结构。

[说明]
结构图的SWX2200使用了SWX2200-8G,但使用了SWX2200-24G时,也能实现同样的功能。

将模块文件拷贝到路由器中

  1. 模块文件下载到PC中。

    [说明]
    模块文件名与Lua脚本的动作有关,因此请不要修改本,原样使用。

  2. 将USB存储器与PC连接,将下载的模块文件拷贝到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脚本中的repuire函数的引数名称。

    [补充]
    将本例中的模块文件不是用于copy指令,而是用于TFTP,拷贝到RTFS时,请以二进制模式拷贝。

导入Lua脚本的程序

[说明]
从Lua脚本的下载,到执行的程序,请参照导入Lua脚本程序手册。

设定RTX1200

环境变数的设定

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 policy interface group 101 name=Private local lan1
ip policy address group 101 name=Private 192.168.0.0/24
ip policy address group 102 name=Any *
ip policy service group 101 name="Open Services"
ip policy service group 102 name=General dns
ip policy service group 103 name=Mail pop3 smtp
ip policy service group 104 name=IPsec ike esp
ip policy filter 1100 reject-nolog lan1 * * * *
ip policy filter 1110 pass-nolog * * * * 102
ip policy filter 1122 static-pass-nolog * lan1 * * *
ip policy filter 1123 static-pass-nolog * local * * *
ip policy filter 1124 static-pass-log * * 192.168.0.0/24 * http
ip policy filter 1130 pass-nolog * tunnel* * * *
ip policy filter 1140 pass-nolog * pp1 * * *
ip policy filter 1520 pass-log * lan1 * * 101
ip policy filter 1530 static-pass-nolog * local * * 104
ip policy filter 1600 reject-nolog tunnel* * * * *
ip policy filter 1630 pass-nolog * tunnel* * * *
ip policy filter 1640 pass-nolog * local * * *
ip policy filter 1650 pass-nolog * lan1 * * *
ip policy filter 1680 reject-nolog * pp* * * *
ip policy filter 1700 pass-nolog local * * * *
ip policy filter 1710 static-pass-nolog * lan1 * * *
ip policy filter 1750 static-pass-nolog * pp* * * 104
ip policy filter 2000 reject-nolog * * * * *
ip policy filter set 101 name="Internet Access" 1100 [1110 1123 [1124] 1122 1140 1130] 1500 [1520 1530] 1600 [1640 1650 1680 1630] 1700 [1710 1750] 2000
ip policy filter set enable 101
Lua脚本的日程设定

schedule at 1 startup * lua /swx2200_lua_bc_storm_ctl_rtx1200.lua

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 ="(投递方邮件地址)"
}
-- 交换机的状态,传输邮件发送失败等信息的SYSLO等级 (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的传输限制flag
    rtn = swx2200.is_storm_stt(sw)
    if (rtn == true) then
        limit = true
        limit_cnt[idx] = limit_cnt[idx] + 1
        -- 清除SWX2200的传输限制flag
        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(" Model name : %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(" (port %d received large"..
                     "amounts of packets)\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 = "The following switch detects a packet storm"..
                "\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中,模块名称的置换符号使用“?”,但在路由器控制台输入“?”的话,会显示帮助提示,因此在输入“?”之前,需要输入转义程序的 '\' 。

返回顶部Return to Top

网络相关产品

服务支持

事业绍介