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