外部程式利用 API 與 RouterOS 溝通

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程式碼如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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

使用方式如下

1
2
3
4
5
6
7
8
9
10
11
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]

3 thoughts on “外部程式利用 API 與 RouterOS 溝通

  1. 你好,看了你的日志很受启发,有个疑问能否帮解答一下?
    程序退出的时候,ros里的active user仍然记录着我的登录信息,该怎么办哈,我已经使用了 close方法

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *