四月 06

top 是一個在 linux 中監看系統狀況的指令

要在 RouterOS 3.x 中加入top,首先請先下載這個封裝檔 (原作者:ksw520)

如果還沒把 RouterOS 的 ftp 功能打開,先到 IP -> Service 把 ftp 開啟

解開壓縮後利用 ftp 把 procps3.x.npk 丟到RouterOS 上去

重新啟動 RouterOS ,就會自動安裝了

接下來使用 pietty 或 putty 來連線

IP:RouterOS 的位置

Port:12345

帳號:metro

密碼:2911911

進去後可用 top 指令來監看系統狀況

如果要改密碼,輸入 password 就可以改密碼了

參考資料:ros下的top命令補丁





三月 30

先把 RouterOS 的管理工具 Winbox 開起來

點選 IP ==> Firewall ==> NAT

接下來就看圖說故事吧

進入 NAT 後,加入一條規則(就是按那個 + )

在 General 裡,Chain 選擇 srcnat

Action 中選擇 masquerad,接下來按下 OK

最後結果如下圖

收工,簡單又快樂。

如果有需要轉Port的功能請參考在RouterOS中加入Port Mapping這篇文章





三月 20

RouterOS 看來應該是會一直玩下去了。越玩越順手

先補上在 WordPress 中可以讓 RouterOS 語法高亮的 SyntaxHighlighter-Plus 樣版

使用的方法可以參考之前的文章幫SyntaxHighlighter-Plus加上新的語言樣版

程式碼另存為shBrushRouterOS.js

SyntaxHighlighter.brushes.RouterOS = function()
{

    var keywords =  'global local do else for from to step in foreach ' +
                    'if put while environment nothing set';

    var commands =  'add comment disable enable export get move remove unset ' +
                    'delay edit blink monitor beep find led len  ' +
                    'list log pick resolve time print toid totime tonum';

        this.regexList = [
                { regex: /#.*$/gm, css: 'comments' },
                { regex: SyntaxHighlighter.regexLib.doubleQuotedString,                 css: 'string' },                // double quoted strings
                { regex: new RegExp(this.getKeywords(keywords), 'gm'),                  css: 'keyword' },               // keywords
                { regex: new RegExp(this.getKeywords(commands), 'gm'),                  css: 'functions' }              // commands
                ];
}

SyntaxHighlighter.brushes.RouterOS.prototype    = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.RouterOS.aliases      = ['ros'];

記得要在/wp-content/plugins/syntaxhighlighter-plus/syntaxhighlighter.php
大約在在112行的位置加入樣版的名子

'RouterOS'=> array('ros'),





三月 14

讓RouterOS自動把Log檔Mail出去,這個動作可以拆成2個步驟來執行

1.在System->Script建立相關的Script(產生Log檔案,發送mail)

2.在System->Scheduler建立排程(判斷Log的行數到達我們的設定值就Mail出去)

第1個部份,先建立2個Script

建立一個名為MailLog的Script

請修改SMTP伺服器位置(記得ROS的DNS要有,才可以解析)、寄件人、收件人的資訊


#Script在RouterOS 3.20中測試通過
#Date:2009/03/14
#Script Name:MailLog

#SMTP伺服器位置
:local SMTP "xxx.smtp.com"

#寄件人
:local MailFrom "ROSLog<ROS@xxxxxxxxx.com>"

#收件人
:local MailTo "REC@xxxxxxxxx.com"

#Log檔的名稱(位於/file)
:local LogFileName

#寄出後是否刪除已存檔的Log(/file裡的檔案)
#0=保留  1=刪除
:local DeleteLogFile 1

:set LogFileName ([/system identity get name] . "Log-" . [:pick [/system clock get date] 7 11] . [:pick [/system clock get date] 0 3] . [:pick [/system clock get date] 4 6] . ".txt");

/tool e-mail set from=$MailFrom;

/log print file=[$LogFileName];

/tool e-mail send to=$MailTo server=[:resolve $SMTP] subject=([/system identity get name] . " Log " . [/system clock get date]) file=[$LogFileName];

:delay 10;

:if ($DeleteLogFile =1) do={
/file rem [/file find name=($LogFileName)];
}

:log info ("System Log emailed at " . [/sys cl get time] . " " . [/sys cl get date]);

接下來建立名為AutoMailLog的Script

超過幾條就把Log寄出去請依照你的實際需要設置

寄出後會自動清空Log,如果你想保留Log

那就把:local IsClearLog 1 改為:local IsClearLog 0

不過這樣子會造成每次都會寄發Mail,請特別注意


#Script在RouterOS 3.20中測試通過
#Date:2009/03/14
#Script Name:AutoMailLog

#超過幾條就把Log寄出去
:local MaxLine 300

#寄出後是否要清空現有的Log資訊
#0=保留  1=清空
:local IsClearLog 1

:if ([/log print count-only] >= $MaxLine) \
do={
/system script run MailLog;
:if ($IsClearLog = 1) \
do={/system script run ClearMemoryLog;}
}

第2個部份是建立Scheduler排程

在System->Scheduler 新增一個名為MailLog的排程

內容只有一行,時間是我是設定為5分鍾跑一次


/system script run AutoMailLog

最後總結一下,整個流程如下圖

參考資料:http://forum.mikrotik.com/viewtopic.php?f=9&t=29122





三月 12

RouterOS 在3.0版以後推出了API可以讓外部程式存取

這樣子功能性真的是延伸了很多

目前API支援以下幾種程式語言

in Perl - forum thread by cheesegrits
in Delphi - forum thread and wiki by rodolfo
in PHP - wiki link by Denis Basta
Java sample methods - forum post
in Python - wiki link by Mikrotik staff
in C# - wiki link by wiki user Gregy

可惜沒有VB.NET的版本。不過只要有C#的版本,一切就好辦事了

以下是轉換過後VB.NET版的API

不過傳送或讀取的值如果是中文的話會變成亂碼,這部份要再研究一下

使用前記得先開啟API的服務

/ip service enable api

或是利用圖型介面開啟 IP->Service

VB.NET版本的RouterOS API程式碼如下

Imports System.Net.Sockets
Imports System.IO
Imports System.Text

Class MK_ROS
    Private connection As Stream
    Private con As TcpClient

    Public Sub New(ByVal ip As String)
        con = New TcpClient()
        con.Connect(ip, 8728)
        connection = DirectCast(con.GetStream(), Stream)
    End Sub
    Public Sub Close()
        connection.Close()
        con.Close()
    End Sub
    Public Function Login(ByVal username As String, ByVal password As String) As Boolean
        Send("/login", True)
        Dim hash As String = Read()(0).Split(New String() {"ret="}, StringSplitOptions.None)(1)
        Send("/login")
        Send("=name=" & username)
        Send("=response=00" & EncodePassword(password, hash), True)
        If Read()(0) = "!done" Then
            Return True
        Else
            Return False
        End If
    End Function
    Public Sub Send(ByVal co As String)
        Dim bajty As Byte() = Encoding.ASCII.GetBytes(co.ToCharArray())
        Dim velikost As Byte() = EncodeLength(bajty.Length)

        connection.Write(velikost, 0, velikost.Length)
        connection.Write(bajty, 0, bajty.Length)
    End Sub
    Public Sub Send(ByVal co As String, ByVal endsentence As Boolean)
        Dim bajty As Byte() = Encoding.ASCII.GetBytes(co.ToCharArray())
        Dim velikost As Byte() = EncodeLength(bajty.Length)
        connection.Write(velikost, 0, velikost.Length)
        connection.Write(bajty, 0, bajty.Length)
        connection.WriteByte(0)
    End Sub
    Public Function Read() As List(Of String)
        Dim output As New List(Of String)()
        Dim o As String = ""
        Dim tmp As Byte() = New Byte(3) {}
        Dim count As Long
        While True
            tmp(3) = CByte(connection.ReadByte())
            'if(tmp[3] == 220) tmp[3] = (byte)connection.ReadByte(); it sometimes happend to me that
            'mikrotik send 220 as some kind of "bonus" between words, this fixed things, not sure about it though
            If tmp(3) = 0 Then
                output.Add(o)
                If o.Substring(0, 5) = "!done" Then
                    Exit While
                Else
                    o = ""
                    Continue While
                End If
            Else
                If tmp(3) < &H80 Then
                    count = tmp(3)
                Else
                    If tmp(3) < &HC0 Then
                        Dim tmpi As Integer = BitConverter.ToInt32(New Byte() {CByte(connection.ReadByte()), tmp(3), 0, 0}, 0)
                        count = tmpi Xor &H8000
                    Else
                        If tmp(3) < &HE0 Then
                            tmp(2) = CByte(connection.ReadByte())
                            Dim tmpi As Integer = BitConverter.ToInt32(New Byte() {CByte(connection.ReadByte()), tmp(2), tmp(3), 0}, 0)
                            count = tmpi Xor &HC00000
                        Else
                            If tmp(3) < &HF0 Then
                                tmp(2) = CByte(connection.ReadByte())
                                tmp(1) = CByte(connection.ReadByte())
                                Dim tmpi As Integer = BitConverter.ToInt32(New Byte() {CByte(connection.ReadByte()), tmp(1), tmp(2), tmp(3)}, 0)
                                count = tmpi Xor &HE0000000
                            Else
                                If tmp(3) = &HF0 Then
                                    tmp(3) = CByte(connection.ReadByte())
                                    tmp(2) = CByte(connection.ReadByte())
                                    tmp(1) = CByte(connection.ReadByte())
                                    tmp(0) = CByte(connection.ReadByte())
                                    count = BitConverter.ToInt32(tmp, 0)
                                Else
                                    'Error in packet reception, unknown length
                                    Exit While
                                End If
                            End If
                        End If
                    End If
                End If
            End If

            For i As Integer = 0 To count - 1
                o += ChrW(connection.ReadByte())
            Next
        End While
        Return output
    End Function
    Private Function EncodeLength(ByVal delka As Integer) As Byte()
        If delka < &H80 Then
            Dim tmp As Byte() = BitConverter.GetBytes(delka)
            Return New Byte(0) {tmp(0)}
        End If
        If delka < &H4000 Then
            Dim tmp As Byte() = BitConverter.GetBytes(delka Or &H8000)
            Return New Byte(1) {tmp(1), tmp(0)}
        End If
        If delka < &H200000 Then
            Dim tmp As Byte() = BitConverter.GetBytes(delka Or &HC00000)
            Return New Byte(2) {tmp(2), tmp(1), tmp(0)}
        End If
        If delka < &H10000000 Then
            Dim tmp As Byte() = BitConverter.GetBytes(delka Or &HE0000000)
            Return New Byte(3) {tmp(3), tmp(2), tmp(1), tmp(0)}
        Else
            Dim tmp As Byte() = BitConverter.GetBytes(delka)
            Return New Byte(4) {&HF0, tmp(3), tmp(2), tmp(1), tmp(0)}
        End If
    End Function

    Public Function EncodePassword(ByVal Password As String, ByVal hash As String) As String
        Dim hash_byte As Byte() = New Byte(hash.Length / 2 - 1) {}
        For i As Integer = 0 To hash.Length - 2 Step 2
            hash_byte(i / 2) = [Byte].Parse(hash.Substring(i, 2), System.Globalization.NumberStyles.HexNumber)
        Next
        Dim heslo As Byte() = New Byte(1 + Password.Length + (hash_byte.Length - 1)) {}
        heslo(0) = 0
        Encoding.ASCII.GetBytes(Password.ToCharArray()).CopyTo(heslo, 1)
        hash_byte.CopyTo(heslo, 1 + Password.Length)

        Dim hotovo As Byte()
        Dim md5 As System.Security.Cryptography.MD5

        md5 = New System.Security.Cryptography.MD5CryptoServiceProvider()

        hotovo = md5.ComputeHash(heslo)

        'Convert encoded bytes back to a 'readable' string
        Dim navrat As String = ""
        For Each h As Byte In hotovo
            navrat += h.ToString("x2")
        Next
        Return navrat
    End Function
End Class

使用方式如下

Dim mikrotik As New MK_ROS("主機位置(可為IP或Domain)")
If Not mikrotik.Login("帳號", "密碼") Then
    TextBox1.Text &= ("連線失敗")
    mikrotik.Close()
    Return
End If
'要下的指令
mikrotik.Send("/system/identity/print", True)
For Each h As String In mikrotik.Read()
    TextBox1.Text &= h
Next

參考資料:

Mikrotik RouterOS v3.x features

RouterOS API

RouterOS Community Support [Scripting]