RouterOS是一種路由操作系統,并通過該軟件將標準的PC電腦變成專用的路由器,在軟件的開發和應用上不斷的更新和發展,軟件經歷了多次更新和改進,使其功能在不斷增強和完善。特別在無線、認證、策略路由、帶寬控制和防火墻過濾等功能上有著非常突出的功能,其極高的性價比,受到許多網絡人士的青睞。
目錄
1,RouterOS 簡介
- RouterOS的簡單介紹
2,RouterOS API
- 介紹了RouterOS API通信與協議原理
3,RouterOS API 客戶端
- Mikrotik -java: RouterOS API 協議的Java客戶端實現
一:RouterOS 簡介
RouterOS是一種路由操作系統,并通過該軟件將標準的PC電腦變成專用的路由器,在軟件的開發和應用上不斷的更新和發展,軟件經歷了多次更新和改進,使其功能在不斷增強和完善。特別在無線、認證、策略路由、帶寬控制和防火墻過濾等功能上有著非常突出的功能,其極高的性價比,受到許多網絡人士的青睞。
RouterOS 是 由拉脫維亞MikroTik 司開發的一 種基于Linux 內核的路由操作系統。RouterOS在具備現有路由系統的大部分功能,能針對網吧、企業、小型ISP接入商、社區等網絡設備的接入,Mikrotik廠家提供帶有RouterOS的硬件路由器,同時支持標準的x86構架PC。一臺586PC機就可以實現路由功能,提高硬件性能同樣也能提高網絡的訪問速度和吞吐量。完全是一套低成本,高性能的路由器系統。
MikroTik RouterOS 是將標準的PC電腦變成功能強大的路由器,添加標準的PC網絡接口卡能增強路由器的功能。MikroTik RouterOS基于路由、PPPoE認證、Web認證、流量控制、Web-proxy、專業無線等于一身 ,可以根據需要增加或刪除相應的功能,是許多路由器所無法實現的。同時MikroTik RouterBOARD專門為RouterOS設計的路由硬件,能穩定的應用在各種網絡環境中。
二:RouterOS API
RouterOS API 允許用戶創建自定義軟件解決方案,與RouterOS通信,以收集信息、調整配置和管理路由器。API嚴格遵循命令行接口(CLI)的語法。它可以用來創建翻譯或自定義的配置工具,幫助使用RouterOS輕松運行和管理路由器。
默認情況下,API使用端口#8728,并且服務已啟用。
API協議
-
簡述
與路由器的通信是通過向路由器發送句子并接收一個或多個句子來完成的。句子是以零長度的單詞結尾的單詞序列。單詞是以某種方式編碼的句子的一部分——編碼長度和數據。通信是通過向路由器發送句子并接收對已發送句子的回復來進行的。使用API發送給路由器的每個句子都應該包含命令,第一個單詞后面跟著沒有特定順序的單詞,句子的結尾用零長度的單詞標記。當路由器收到完整的句子(命令字,沒有或多個屬性字,零長度字)時,它被評估和執行,然后形成應答并返回。
-
API words
單詞是句子的一部分。每個單詞都必須以特定的方式編碼——單詞的長度后跟單詞的內容。字的長度應以將要發送的字節數表示。
Value of length # of bytes Encoding 0 <= len <= 0x7F 1 len, lowest byte 0x80 <= len <= 0x3FFF 2 len | 0x8000, two lower bytes 0x4000 <= len <= 0x1FFFFF 3 len | 0xC00000, three lower bytes 0x200000 <= len <= 0xFFFFFFF 4 len | 0xE0000000 len >= 0x10000000 5 0xF0 and len as four bytes 每個單詞被編碼為長度,后面跟著相同字節的內容;
單詞被組合成句子。句子結束時以零長度詞結尾;
方案允許長度為0x7FFFFFFFFF的編碼,只支持四個字節的長度;
len的字節首先發送最重要的字節(網絡順序);
如果字的第一個字節是>= 0xF8,則它是一個保留控制字節。在接收到未知的控制字節后,API客戶端不能繼續,因為它不知道如何解釋下面的字節;
-
一般來說,單詞可以這樣描述<<encoded word length><word content>>,
單詞內容可以分為5個部分:命令詞、屬性詞、API屬性詞、查詢詞和回復詞
-
命令詞 (Command word)
句子中的第一個單詞必須是command,后跟屬性詞和零長詞或終止詞。命令字的名稱應以“/”開頭。命令名緊跟在CLI后面,空格替換為“/”。有一些特定于API的命令;
命令詞結構有嚴格的順序:
編碼長度
內容前綴/
-
CLI轉換命令
/login /user/active/listen /interface/vlan/remove /system/reboot
-
屬性詞(Attribute word)
根據內容,每個命令詞都有自己的屬性詞列表。
Atribute單詞結構由5部分組成,按順序排列:
- 編碼長度
- 內容前綴等于符號-=
- 屬性名
- 等號分隔-=
- 屬性的值(如果有),該屬性可能沒有值(由于單詞的編碼方式不同,Value可以在屬性詞的值中包含多個等號)
沒有編碼長度前綴的示例:
=address=10.0.0.1 =name=iu=c3Eeg =disable-running-check=yes
警告:屬性詞和API參數的順序并不重要,不應依賴它們
-
API屬性詞(API attribute word)
API屬性詞結構的順序非常嚴格:
- 編碼長度
- 帶點的內容前綴。
- 屬性名
- 名稱用等號=符號進行后綴
- 屬性值
目前唯一這樣的API屬性是tag
注意:如果句子包含API屬性單詞標記,那么路由器返回給該標記句子的每個句子都將使用相同的標記進行標記。
-
查詢詞(Query word)
句子可以有額外的查詢參數來限制其范圍。
使用查詢詞屬性的句子示例:
/interface/print ?type=ether ?type=vlan ?#|!
查詢詞以“?”開頭。
目前只有print命令處理查詢詞。
注意:查詢詞是有順序的
-
回復詞(Reply word)
它只由路由器發送。它只在客戶發送完整句子后發送。
- 回復的第一個詞以“!”開頭;
- 發送的每個句子至少會生成一個回復(如果連接沒有終止);
- 每句話的最后一個回復是第一個單詞的回復!完成;
- 錯誤和異常情況始于!陷阱
- 數據回復開始!重新
- 如果API連接關閉,RouterOS將發送!以原因作為回復,然后關閉連接;
API語句
API語句是使用API進行通信的主要對象。
- 空話被忽略。
- 在接收到零長單詞后,對句子進行處理。
- 客戶端在登錄之前可以發送的句子數量和大小有限制。
- 屬性詞的順序不應依賴。因為順序和數量是可以改變的。proplist屬性。
- 句子結構如下:
- 第一個單詞應該包含命令詞;
- 應該包含長度為零的單詞來終止句子;
- 可以不包含或包含多個屬性詞。句子中的定語詞沒有特定的順序,順序對定語詞并不重要;
- 不能包含任何或多個查詢詞。句子中疑問詞的順序很重要。
注意:零長單詞結束句子。如果未提供,路由器將不會開始評估發送的單詞,并將所有輸入視為同一句子的一部分。
初始登錄
- 首先,客戶端發送/login命令。
- 回復包含=ret=challenge參數。
- 客戶端發送第二個/login命令,其中包含=name=username和=response=response。
- 如果出現錯誤,reply包含=ret=錯誤消息。
- 如果成功登錄,客戶端可以開始發出命令。
標簽
- 可以同時運行多個命令,而無需等待前一個命令完成。如果API客戶端正在這樣做,并且需要區分命令響應,那么它可以在命令語句中使用“tag”API參數。
- 如果在命令語句中包含具有非空值的“tag”參數,則具有完全相同值的“tag”參數將包含在此命令生成的所有響應中。
- 如果不包含“tag”參數或其值為空,則此命令的所有響應都不會包含“tag”參數。
命令
/cancel
- 可選參數:=tag=要取消的命令的標記,如果沒有它,將取消所有正在運行的命令不會自動取消
- 所有被取消的命令都會被中斷,通常情況下會生成'!陷阱'和'!“完成”的回答
- 請注意/cancel是單獨的命令,可以有自己的“唯一”。tag'參數,該參數與此命令的“=tag”參數無關
/listen
- listen命令在控制臺打印命令可用的地方可用,但它在任何地方都沒有預期的效果(即可能不起作用)
- !當特定項目列表中的某些內容發生變化時,會生成復句
- 當項目被刪除或以任何其他方式消失時,“!re‘句子包含值’=。死亡=是'
- 此命令不會終止。要終止它,請使用/cancel命令。
/getall
- 當控制臺打印命令可用時,getall命令可用。由于版本3.21,getall是print的別名。
- 回復包含=。id=物料內部編號屬性。
API打印命令與控制臺對應命令的不同之處如下:
- 在不支持參數的情況下。可以使用查詢詞篩選項目(見下文)。
- .proplist參數是一個逗號分隔的屬性名稱列表,應該包含在返回的項中。
- 返回的項目可能具有其他屬性。
- 未定義返回屬性的順序。
- 如果列表包含重復條目,則不定義對此類條目的處理。
- 如果有屬性值存在。proplist,但不在該項中,則該項沒有此屬性值(?該項的名稱將計算為false)。
- 如果proplist不存在,所有屬性都會按照print命令的要求包括在內,即使是那些訪問時間較慢的屬性(例如文件內容和性能計數器)。因此使用。支持proplist。省略。如果設置了=detail=參數,proplist可能會有較高的性能損失。
查詢
print命令接受限制返回語句集的查詢詞。此功能自RouterOS 3.21開始提供。
查詢詞以“?”開頭。
查詢詞的順序很重要。查詢從第一個單詞開始計算。
將為列表中的每個項計算查詢。如果查詢成功,則處理項;如果查詢失敗,則忽略項。
查詢使用布爾值堆棧進行計算。最初,堆棧包含無限量的“真”值。在計算結束時,如果堆棧至少包含一個“false”值,則查詢失敗。
-
查詢詞的操作遵循以下規則:
查詢語句 描述 ?name 如果項的屬性值為name,則推送'true',否則推送'false'。 ?-name 如果項沒有屬性名稱的值,則按“true”,否則按“false”。 ?name=x ?=name=x 如果屬性name的值等于x,則推送'true',否則推送'false'。 ?<name=x 如果屬性name的值小于x,則推送'true',否則推送'false'。 ?>name=x 如果屬性name的值大于x,則推送'true',否則推送'false'。 ?#operations 對堆棧中的值應用操作。
操作字符串從左到右計算。
后跟任何其他字符或字尾的十進制數字序列被解釋為堆棧索引。
Top值的索引為0。
后跟字符的索引在該索引處推送值的副本。后跟單詞結尾的索引將用該索引處的值替換所有值。
!字符用相反的值替換最高的值。
&彈出兩個值并推入邏輯'and'操作的結果。
|彈出兩個值并推入邏輯'or'操作的結果。
.后索引什么都不做。
.在另一個字符推送頂部值的拷貝后。注意:API中不支持正則表達式,因此不要嘗試發送帶有~符號的查詢
例如:獲取所有以太網和VLAN接口:/interface/print ?type=ether ?type=vlan ?#|
獲取所有帶有非空注釋的路由:
/ip/route/print ?>comment=
!trap
當由于某些原因API語句失敗時,trap被返回,并伴隨著message屬性和某些情況下category參數
-
message
<<< /ip/address/add <<< =address=192.168.88.1 <<< =interface=asdf <<< >>> !trap >>> =category=1 >>> =message=input does not match any value of interface
-
category
如果是一般錯誤,則對其進行分類并返回錯誤類別。此屬性的可能值為
- 缺少項目或命令
- 參數值失敗
- 命令執行中斷
- 腳本相關的失敗
- 一般故障
- API相關故障
- TTY相關故障
- 通過:return命令生成的值
三:RouterOS API 客戶端
Mikrotik RouterOS API的Java客戶端庫實現。
這個項目提供了一個Java客戶機來使用遠程API操作Mikrotik路由器。
開源地址:https://github.com/GideonLeGrange/mikrotik-java
maven依賴
<dependency>
<groupId>me.legrange</groupId>
<artifactId>mikrotik</artifactId>
<version>3.0.7</version>
</dependency>
如何使用API最好通過示例來說明。
這些例子應該說明如何使用這個庫。請注意,假設用戶精通Java并理解Mikrotik命令行語法。命令行語法告訴你可以傳遞哪些命令,但是這個庫使用的RouterOS API并不支持所有的命令。
在調試API調用時需要考慮以下幾點:
- RouterOS API不支持自動完成。您需要寫出命令和參數名稱。例如,你不能說/ip/hotspot/user/add name=john add=10.0.0.1,你需要寫出地址。
- 您需要在值中加上空格。你不能說name=Joe Blogs,你需要使用name="Joe Blogs"
- 根因為ApiCommandException的異常是指從遠端RouterOS設備接收到的錯誤信息。
-
建立連接
ApiConnection con = ApiConnection.connect("10.0.1.1"); // connect to router con.login("admin","password"); // log in to router con.execute("/system/reboot"); // execute a command con.close(); // disconnect from router
上面的示例展示了一種使用默認API端口和超時創建未加密連接的簡單方法,這對于開發和測試非常有用。
TLS
對于生產環境,建議對API流量進行加密。要做到這一點,你需要通過傳遞一個SocketFactory實例來打開到路由器的TLS連接,SocketFactory實例是你希望用來構造TLS套接字的API
ApiConnection con = ApiConnection.connect(SSLSocketFactory.getDefault(), "10.0.1.1", ApiConnection.DEFAULT_TLS_PORT, ApiConnection.DEFAULT_CONNECTION_TIMEOUT);
上面的默認SSL套接字工廠實例被傳遞給API。只要路由器的證書已經添加到本地密鑰存儲中,就可以這樣做。除了允許用戶指定套接字工廠外,上面的方法還提供了對TCP端口和連接超時的完全控制。
RouterOS還支持匿名TLS。
連接超時
默認情況下,如果API不能連接到指定的路由器,它將生成一個異常。這可以立即發生(通常是在操作系統返回“連接拒絕”錯誤時),但如果路由器主機有防火墻或有其他網絡問題,也可能需要長達60秒。這60秒是“默認連接超時”,可以通過將首選超時作為connect()調用的最后一個參數傳遞給APi來覆蓋。例如:
ApiConnection con = ApiConnection.connect(SSLSocketFactory.getDefault(), "10.0.1.1", ApiConnection.DEFAULT_TLS_PORT, 2000); // connect to router on the default API port and fail in 2 seconds
常量
ApiConnection中提供了一些常量,讓用戶更容易使用默認端口和超時來構建連接:
常量 描述 值 DEFAULT_PORT 未加密連接的默認TCP“端口”值 8728 DEFAULT_TLS_PORT 加密連接的默認TCP“端口”值 8729 DEFAULT_CONNECTION_TIMEOUT 默認連接超時值(毫秒) 60000 -
讀數據
一個返回結果的簡單示例-打印所有接口:
List<Map<String, String>> rs = con.execute("/interface/print"); for (Map<String,String> r : rs) { System.out.println(r); }
結果作為String鍵/值對的映射列表返回。這樣做的原因是,一個命令可以返回多個結果,這些結果有多個堆變量。例如,要打印上面命令中返回的所有接口的名稱,執行:
for (Map<String, String> map : rs) { System.out.println(map.get("name")); }
過濾結果:
相同的查詢,但過濾了結果:打印所有類型為“vlan”的接口。
List<Map<String, String>> rs = con.execute("/interface/print where type=vlan");
選擇返回的字段:
打印所有類型為“vlan”的接口,并只返回它們的名稱:
List<Map<String, String>> rs = con.execute("/interface/print where type=vlan return name");
-
寫數據
本例展示如何新建GRE接口:
con.execute("/interface/gre/add remote-address=192.168.1.1 name=gre1 keepalive=10");
修改上面例子中創建的對象的IP地址:
con.execute("/interface/gre/set .id=gre1 remote-address=10.0.1.1");
現在移除對象:
con.execute("/interface/gre/remove .id=gre1");
取消對象上的變量設置
取消變量的設置略有不同,您需要使用一個名為value-name的參數。這并沒有很好的記錄。假設你有這樣一個防火墻規則:
con.execute("/ip/firewall/filter/add action=accept chain=forward time=00:00:01-01,mon")
假設規則可以被訪問為.id=*1,你可以使用value-name取消它的設置,如下所示:
con.execute("/ip/firewall/filter/unset .id=*1 value-name=time");
-
異步執行命令
我們可以異步運行一些命令來繼續接收更新:
這個例子展示了如何運行'/interface wireless monitor',并將結果發送給監聽器對象,由它打印出來:
String tag = con.execute("/interface/wireless/monitor .id=wlan1 return signal-to-noise", new ResultListener() { public void receive(Map<String, String> result) { System.out.println(result); } public void error(MikrotikApiException e) { System.out.println("An error occurred: " + e.getMessage()); } public void completed() { System.out.println("Asynchronous command has finished"); } } );
ResultListener接口有三個用戶需要實現的方法:
- 調用receive()來接收由路由器從API產生的結果。
- 當基于從路由器接收到的“陷阱”或其他(通常是連接)問題引發異常時,將調用Error()。
- 當路由器指示命令已完成或已取消時,將調用Completed()。
上述命令將在結果可用時以異步方式運行并發送結果,直到它被取消。命令(由唯一返回的String標識)被這樣取消:
con.cancel(tag);
-
命令超時設置
命令超時可用于確保同步命令在特定時間內返回或失敗。命令超時與connect()中使用的連接超時是分開的,可以使用setTimeout()來設置。下面是一個例子:completed()在路由器指示命令已完成或已取消時被調用。
ApiConnection con = ApiConnection.connect("10.0.1.1"); // connect to router con.setTimeout(5000); // set command timeout to 5 seconds con.login("admin","password"); // log in to router con.execute("/system/reboot"); // execute a command
如果用戶未設置命令超時時間,則命令超時時間默認為60秒。
一些信息
路漫漫其修遠兮,吾將上下而求索
碼云:https://gitee.com/javacoo
QQ群:164863067
作者/微信:javacoo
郵箱:xihuady@126.com