LANインタフェースの回線使用率を監視する(FWX120を利用)

図 LANインタフェースの回線使用率を監視する

定期的にLANインタフェース(CATV型接続など)の回線使用率を監視し、送信負荷率または受信負荷率が閾値を超えた場合にメールで通知するLuaスクリプトです。

FWX120の設定例

LANの
インタフェースの設定
(LAN1ポートを使用)
ip lan1 address 192.168.0.1/24
WANの
インタフェースの設定
(LAN2ポートを使用)
ip lan2 address dhcp
ip lan2 nat descriptor 1
ip route default gateway dhcp lan2
NATの設定 nat descriptor type 1 masquerade
nat descriptor address outer 1 primary
DHCPの設定 dhcp service server
dhcp scope 1 192.168.0.2-192.168.0.100/24
DNSの設定 dns server (ISPより指定されたDNSサーバーのIPアドレス)
dns private address spoof on
フィルタの設定 ip inbound filter 1001 reject-nolog * * tcp,udp * 135
ip inbound filter 1002 reject-nolog * * tcp,udp 135 *
ip inbound filter 1003 reject-nolog * * tcp,udp * netbios_ns-netbios_ssn
ip inbound filter 1004 reject-nolog * * tcp,udp netbios_ns-netbios_ssn *
ip inbound filter 1005 reject-nolog * * tcp,udp * 445
ip inbound filter 1006 reject-nolog * * tcp,udp 445 *
ip inbound filter 1007 reject-nolog 192.168.0.0/24 * * * *
ip inbound filter 1099 pass-nolog * * * * *
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 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 1150 pass-nolog * pp1 * * *
ip policy filter 1500 reject-nolog pp* * * * *
ip policy filter 1520 pass-log * lan1 * * 101
ip policy filter 1700 pass-nolog local * * * *
ip policy filter 1710 static-pass-nolog * lan1 * * *
ip policy filter 3000 reject-nolog * * * * *
ip policy filter set 101 name="Internet Access" 1100 [1110 1123 [1124] 1122 1150] 1500 [1520] 1700 [1710] 3000
ip policy filter set enable 101
ip lan2 inbound filter list 1001 1002 1003 1004 1005 1006 1007 1099
Luaスクリプトのスケジュール設定 schedule at 1 startup * lua (Luaスクリプトファイル名)

Luaスクリプト例

設定値

-- 監視間隔 (1-864000 秒)
idle_time = (監視間隔)

-- 回線使用率を計測する時間(1, 2 .. 秒)
avr_sec = (計測時間)

-- 監視するLANインターフェイスの番号
lan_num = (LANインターフェイスの番号)

-- 送信負荷率の閾値(1 - 99 %)
snd_th = (負荷率)

-- 受信負荷率の閾値(1 - 99 %)
rcv_th = (負荷率)

-- 連続で閾値を超えたら異常と判断する回数、または正常な状態に復帰したと判断する回数(1, 2 ..)
count = (回数)

-- 正常な状態に復帰した場合にもメールを送るか否か(送る:true / 送らない:false)
down_mail = (true / false)

-- メールの設定
mail_tbl = {
 smtp_address = "(SMTPサーバーのアドレス)",
 from = "(送信元メールアドレス)",
 to = "(宛先メールアドレス)"
}

-- メールの送信に失敗した時に出力する SYSLOG のレベル(info, debug, notice)
log_level = "(SYSLOGレベル)"

LANインターフェイスの速度を求める関数

function interface_speed(num)
 local rtn, str, val, rt_name
 local cmd = "show config"
 local ptn = "speed lan" .. tostring(num) .. " (%d+%a)"

 rtn, str = rt.command(cmd)
 if (rtn) and (str) then
  str = str:match(ptn)
  if (str) then
   val = unitstr2num(str)
  end
 end

 if (not val) or (val == 0) then
  rt_name = string.match(_RT_FIRM_REVISION, "(%w+) ")

  if (rt_name == "RTX1200") or (rt_name == "NVR500") or (rt_name == "FWX120") then
   val = unitstr2num("1000m")
  elseif (rt_name == "SRT100") then
   val = unitstr2num("100m")
  end
 end

 return val
end

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

function unitstr2num(str)
 local val, unit

 val = tonumber(str:match("%d+"))
 unit = str:sub(-1):lower()
 
 if (unit == "k") then
  val = val * 1024
 elseif (unit == "m") then
  val = val * 1024 * 1024
 else
  val = 0
 end

 return val
end

show status lan コマンドの
実行結果から回線使用率を求める関数

function lan_load_info(num, sec)
 local rtn, str1, str2, rcv, snd
 local t = {}
 local cmd = "show status lan" .. tostring(num)
 local ptn = "%((%d+)%s+オクテット"

 rtn, str1 = rt.command(cmd)
 if (rtn) and (str1) then
  rt.sleep(sec)

  rtn, str2 = rt.command(cmd)
  if (rtn) and (str2) then
   str1 = str1 .. str2

   n = 1
   for w in string.gmatch(str1, ptn) do
    t[n] = w
    n = n + 1
   end

   if (t[1]) and (t[3]) then
    snd = ((tonumber(t[3]) - tonumber(t[1])) *8) /sec
   else
    rtn = false
    str1 = "使用帯域(送信)の取得失敗\r\n"
   end
   if (t[2]) and (t[4]) then
    rcv = ((tonumber(t[4]) - tonumber(t[2])) *8) /sec
   else
    rtn = false
    str1 = "使用帯域(受信)の取得失敗\r\n"
   end
  else
   str1 = str2
  end
 else
  str1 = cmd .. "コマンドの実行失敗\r\n"
 end

 return rtn, rcv, snd, str1
end

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

function make_lanmsg(tbl, val, th, down)
 local rtn
 local str = ""

 if (val) then
  rtn = count_proc(tbl, val, th)
  if (rtn < 0) then
   if (down) then
    str = tbl.title .. "負荷率が閾値以下の値に下がりました。"
   end
  elseif (rtn > 0) then
   str = tbl.title .. "負荷率が閾値を超えました。\r\n"
   str = str .. string.format(" %s負荷率: %d%%\r\n 閾値: %d%%\r\n\r\n",
      tbl.title, val, th)
  end
 end

 return str
end

閾値を超えた、または下がった
連続回数をカウントする関数

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

 if (val > th) then
  if (not t.flag) then
   t.over = t.over + 1
   if (t.over == count) then
    rtn = 1
    t.flag = true
   end
  else
   if (t.down > 0) then
    t.down = 0
   end
  end
 else
  if (t.flag) then
   t.down = t.down + 1
   if (t.down == count) then
    rtn = -1
    t.flag = false
    t.over = 0
    t.down = 0
   end
  else
   if (t.over > 0) then
    t.over = 0
   end
  end
 end

 return rtn
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

メインルーチン

local rtn, str, max
local snd, rcv
local cnt_tbl = {
 rcv = {over = 0, down = 0, flag = false, title = "受信"},
 snd = {over = 0, down = 0, flag = false, title = "送信"}
}
max = interface_speed(lan_num)

while (true) do
 mail_tbl.text = ""

 rtn, rcv, snd, str = lan_load_info(lan_num, avr_sec)
 if (rtn) then
  snd = 100*snd/max
  rcv = 100*rcv/max
  mail_tbl.text = mail_tbl.text .. make_lanmsg(cnt_tbl.rcv, rcv, rcv_th, down_mail)
  mail_tbl.text = mail_tbl.text .. make_lanmsg(cnt_tbl.snd, snd, snd_th, down_mail)
 else
  mail_tbl.text = str
 end

 if (mail_tbl.text:len() > 0) then
  mail_tbl.subject = string.format("[LAN %d] transmit/receive load watch (%s)", lan_num, time_stamp())
  rtn = rt.mail(mail_tbl)
  if (not rtn) then
   rt.syslog(log_level, "failed to send mail. (Luaスクリプトファイル名)")
  end
 end

 rt.sleep(idle_time)
end