├── LICENCE ├── README.md ├── mikrotik.go ├── mikrotik_test.go ├── types.go └── values.go /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 sg3des 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mikrotik/RouterOS API Client 2 | 3 | ## WIP 4 | 5 | Common methods fully workable, but not all API elements released. 6 | 7 | 8 | ### install: 9 | 10 | ```go 11 | go get -u github.com/sg3des/mikrotik 12 | ``` 13 | 14 | ### usage: 15 | 16 | ```go 17 | router, err := mikrotik.Dial(addr, user, pass) 18 | // OR 19 | router, err := mikrotik.DialTimeout(addr, user, pass, timeout) 20 | ``` 21 | 22 | API methods are presented how a tree, similar to the CLI commands RouterOS. 23 | 24 | ```go 25 | //get router name 26 | router.System.Identity.Name() 27 | 28 | //add ip address 29 | ip := mikrotik.IPAddress{Address: ..., Interface: ...} 30 | router.IP.Address.Add(&ip) 31 | 32 | //get all network interfaces 33 | var intfs []mikrotik.Interface 34 | router.Interface.List(&intfs) 35 | ``` 36 | 37 | Most methods accepts a pointer on the appropriate structure, example: `mikrotik.IPAddress` , `mikrotik.NATRule` etc... Structure field names can by founded by tag `mikrotik`. If tag not specified, go field name auto convert to RouterOS like format, example: `FieldName` converted to `field-name`, and back. -------------------------------------------------------------------------------- /mikrotik.go: -------------------------------------------------------------------------------- 1 | package mikrotik 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "sync" 7 | "time" 8 | 9 | log "github.com/sirupsen/logrus" 10 | routeros "gopkg.in/routeros.v2" 11 | ) 12 | 13 | // Dial to mikrotik router 14 | func Dial(addr, user, pass string) (*Mikrotik, error) { 15 | c, err := routeros.Dial(addr, user, pass) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | mik := &Mikrotik{Conn: c} 21 | mik.setMikrotikCommands() 22 | 23 | return mik, nil 24 | } 25 | 26 | // DialTimeout dial to mikrotik router with timeout 27 | func DialTimeout(addr, user, pass string, timeout time.Duration) (*Mikrotik, error) { 28 | c, err := routeros.DialTimeout(addr, user, pass, timeout) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | mik := &Mikrotik{Conn: c} 34 | mik.setMikrotikCommands() 35 | 36 | return mik, nil 37 | } 38 | 39 | // Mikrotik is common struct contains connection to device and API-tree 40 | type Mikrotik struct { 41 | Conn *routeros.Client 42 | connMutex sync.Mutex 43 | 44 | IP ip 45 | System system 46 | Interface netinterface 47 | PPP ppp 48 | 49 | debug bool 50 | } 51 | 52 | func (mik *Mikrotik) Debug(debug bool) { 53 | mik.debug = debug 54 | } 55 | 56 | func (mik *Mikrotik) Close() { 57 | mik.Conn.Close() 58 | } 59 | 60 | func (mik *Mikrotik) setMikrotikCommands() { 61 | mik.IP = ip{ 62 | Address: cmd{mikrotik: mik, path: "/ip/address"}, 63 | Route: cmd{mikrotik: mik, path: "/ip/route"}, 64 | Firewall: firewall{ 65 | NAT: cmd{mikrotik: mik, path: "/ip/firewall/nat"}, 66 | Mangle: cmd{mikrotik: mik, path: "/ip/firewall/mangle"}, 67 | }, 68 | } 69 | 70 | mik.System = system{ 71 | mikrotik: mik, 72 | path: "/system", 73 | Identity: identity{mikrotik: mik, path: "/system/identity"}, 74 | NTP: ntp{ 75 | Client: cfg{mikrotik: mik, path: "/system/ntp/client"}, 76 | }, 77 | Routerboard: printable{mikrotik: mik, path: "/system/routerboard"}, 78 | Resource: printable{mikrotik: mik, path: "/system/resource"}, 79 | } 80 | 81 | mik.Interface = netinterface{ 82 | mikrotik: mik, 83 | path: "/interface", 84 | SSTPServer: cmd{mikrotik: mik, path: "/interface/sstp-server"}, 85 | SSTPClient: cmd{mikrotik: mik, path: "/interface/sstp-client"}, 86 | Wireless: wireless{ 87 | cmd: cmd{mikrotik: mik, path: "/interface/wireless"}, 88 | SecurityProfiles: cmd{mikrotik: mik, path: "/interface/wireless/security-profiles"}, 89 | }, 90 | Lte: lte{mikrotik: mik, path: "/interface/lte"}, 91 | Ethernet: cmd{mikrotik: mik, path: "/interface/ethernet"}, 92 | } 93 | 94 | mik.PPP = ppp{ 95 | AAA: cfg{mikrotik: mik, path: "/ppp/aaa"}, 96 | Secret: cmd{mikrotik: mik, path: "/ppp/secret"}, 97 | L2tpSecret: cmd{mikrotik: mik, path: "/ppp/l2tp-secret"}, 98 | Profile: cmd{mikrotik: mik, path: "/ppp/profile"}, 99 | } 100 | } 101 | 102 | func (mik *Mikrotik) Ping(addr string, count int) *Ping { 103 | return &Ping{mikrotik: mik, Address: addr, Count: count} 104 | } 105 | 106 | func (ping *Ping) Start() ([]*PingResponse, error) { 107 | re, err := ping.mikrotik.RunArgs("/ping", ToArgs(ping)...) 108 | if err != nil { 109 | return nil, err 110 | } 111 | 112 | var pingResp []*PingResponse 113 | for _, resp := range re.Re { 114 | ValuesFrom(resp.Map).To(&pingResp) 115 | } 116 | 117 | if len(pingResp) > 0 && pingResp[0].PacketLoss == 100 { 118 | return pingResp, errors.New("timeout") 119 | } 120 | 121 | return pingResp, nil 122 | } 123 | 124 | // Run one line command on mikrotik by api 125 | func (mik *Mikrotik) Run(cmd string) (*routeros.Reply, error) { 126 | mik.connMutex.Lock() 127 | defer mik.connMutex.Unlock() 128 | 129 | log.Tracef("[Run] %v", cmd) 130 | re, err := mik.Conn.Run(cmd) 131 | log.Tracef("[Run](reply) %+v", re) 132 | if err != nil { 133 | mik.Conn.Run("") 134 | } 135 | 136 | return re, err 137 | } 138 | 139 | // RunArgs run many line command on mikrotik by api 140 | func (mik *Mikrotik) RunArgs(cmd string, args ...string) (*routeros.Reply, error) { 141 | mik.connMutex.Lock() 142 | defer mik.connMutex.Unlock() 143 | toRun := append([]string{cmd}, args...) 144 | log.Tracef("[RunArgs] %v", toRun) 145 | re, err := mik.Conn.RunArgs(toRun) 146 | 147 | log.Tracef("[RunArgs](reply) %+v", re) 148 | if err != nil { 149 | mik.Conn.Run("") 150 | } 151 | return re, err 152 | } 153 | 154 | // RunMarshal - run command and marhsal response to interface struct 155 | func (mik *Mikrotik) RunMarshal(cmd string, v interface{}) error { 156 | re, err := mik.Run(cmd) 157 | if err != nil { 158 | return err 159 | } 160 | 161 | return mik.ParseResponce(re, v) 162 | } 163 | 164 | func (mik *Mikrotik) ParseResponce(re *routeros.Reply, v interface{}) error { 165 | for _, resp := range re.Re { 166 | if mik.debug { 167 | log.Debug(resp) 168 | } 169 | 170 | if err := ValuesFrom(resp.Map).To(v); err != nil { 171 | return err 172 | } 173 | } 174 | 175 | return nil 176 | } 177 | 178 | // Print returns all items by apipath and marshal it to passed structure 179 | func (mik *Mikrotik) Print(apipath string, v interface{}) error { 180 | return mik.RunMarshal(apipath, v) 181 | } 182 | 183 | // Add item from passed struct to apipath 184 | func (mik *Mikrotik) Add(apipath string, v interface{}) error { 185 | re, err := mik.RunArgs(apipath, ToArgs(v)...) 186 | if err != nil { 187 | return err 188 | } 189 | 190 | if id, ok := re.Done.Map["ret"]; ok { 191 | SetID(v, id) 192 | } 193 | 194 | return nil 195 | } 196 | 197 | // Set value of item by id 198 | func (mik *Mikrotik) Set(apipath, id string, v interface{}) error { 199 | args := append([]string{"=.id=" + id}, ToArgs(v)...) 200 | _, err := mik.RunArgs(apipath, args...) 201 | return err 202 | } 203 | 204 | // SetOne set value to one field 205 | func (mik *Mikrotik) SetOne(apipath, name, value string) error { 206 | _, err := mik.RunArgs(apipath, fmt.Sprintf("=%s=%s", name, value)) 207 | return err 208 | } 209 | 210 | // Remove item by id 211 | func (mik *Mikrotik) Remove(apipath, id string) error { 212 | _, err := mik.RunArgs(apipath, "=.id="+id) 213 | return err 214 | } 215 | 216 | // Enable item by id 217 | func (mik *Mikrotik) Enable(apipath, id string) error { 218 | _, err := mik.RunArgs(apipath, "=.id="+id) 219 | return err 220 | } 221 | 222 | // Disable item by id 223 | func (mik *Mikrotik) Disable(apipath, id string) error { 224 | _, err := mik.RunArgs(apipath, "=.id="+id) 225 | return err 226 | } 227 | 228 | // Comment - add comment to item by id 229 | func (mik *Mikrotik) Comment(apipath, id, comment string) error { 230 | _, err := mik.RunArgs(apipath, "=.id="+id, "=comment="+comment) 231 | return err 232 | } 233 | 234 | // ==================================== 235 | // 236 | // API tree 237 | // 238 | // ==================================== 239 | 240 | type ip struct { 241 | Address cmd 242 | Route cmd 243 | Firewall firewall 244 | } 245 | 246 | type firewall struct { 247 | NAT cmd 248 | Mangle cmd 249 | } 250 | 251 | // printable allow ony print a struct 252 | type printable struct { 253 | mikrotik *Mikrotik 254 | path string 255 | } 256 | 257 | func (p *printable) Print(v interface{}) error { 258 | return p.mikrotik.Print(p.path+"/print", v) 259 | } 260 | 261 | type cmd struct { 262 | mikrotik *Mikrotik 263 | path string 264 | } 265 | 266 | func (c *cmd) List(v interface{}) error { 267 | return c.mikrotik.Print(c.path+"/print", v) 268 | } 269 | 270 | func (c *cmd) Find(where string, v interface{}) error { 271 | re, err := c.mikrotik.RunArgs(c.path+"/print", "?"+where) 272 | if err != nil { 273 | return err 274 | } 275 | 276 | return c.mikrotik.ParseResponce(re, v) 277 | } 278 | 279 | // func (c *cmd) Get(id string, v interface{}) error { 280 | // re, err := c.mikrotik.RunArgs(c.path+"/print", "where", "?.id="+id) 281 | // if err != nil { 282 | // return err 283 | // } 284 | 285 | // return c.mikrotik.ParseResponce(re, v) 286 | // } 287 | 288 | func (c *cmd) Add(v interface{}) error { 289 | return c.mikrotik.Add(c.path+"/add", v) 290 | } 291 | 292 | func (c *cmd) Set(id string, v interface{}) error { 293 | return c.mikrotik.Set(c.path+"/set", id, v) 294 | } 295 | 296 | func (c *cmd) Remove(id string) error { 297 | return c.mikrotik.Remove(c.path+"/remove", id) 298 | } 299 | 300 | func (c *cmd) Enable(id string) error { 301 | return c.mikrotik.Enable(c.path+"/enable", id) 302 | } 303 | 304 | func (c *cmd) Disable(id string) error { 305 | return c.mikrotik.Disable(c.path+"/disable", id) 306 | } 307 | 308 | func (c *cmd) Comment(id, comment string) error { 309 | return c.mikrotik.Comment(c.path+"/comment", id, comment) 310 | } 311 | 312 | type system struct { 313 | mikrotik *Mikrotik 314 | path string 315 | 316 | Identity identity 317 | NTP ntp 318 | Routerboard printable 319 | Resource printable 320 | } 321 | 322 | type ntp struct { 323 | Client cfg 324 | } 325 | 326 | type cfg struct { 327 | mikrotik *Mikrotik 328 | path string 329 | } 330 | 331 | func (c *cfg) Get(v interface{}) error { 332 | return c.mikrotik.Print(c.path+"/print", v) 333 | } 334 | 335 | func (c *cfg) Set(name, value string) error { 336 | return c.mikrotik.SetOne(c.path+"/set", name, value) 337 | } 338 | 339 | type identity struct { 340 | mikrotik *Mikrotik 341 | path string 342 | } 343 | 344 | func (c *identity) Name() (string, error) { 345 | var resp struct { 346 | Name string 347 | } 348 | err := c.mikrotik.Print(c.path+"/print", &resp) 349 | return resp.Name, err 350 | } 351 | 352 | func (c *identity) SetName(name string) error { 353 | return c.mikrotik.SetOne(c.path+"/set", "name", name) 354 | } 355 | 356 | // netinterface not have add method 357 | type netinterface struct { 358 | mikrotik *Mikrotik 359 | path string 360 | 361 | SSTPClient cmd 362 | SSTPServer cmd 363 | Wireless wireless 364 | Lte lte 365 | Ethernet cmd 366 | } 367 | 368 | func (c *netinterface) List(v interface{}) error { 369 | return c.mikrotik.Print(c.path+"/print", v) 370 | } 371 | 372 | func (c *netinterface) Find(where string, v interface{}) error { 373 | re, err := c.mikrotik.RunArgs(c.path+"/print", "?"+where) 374 | if err != nil { 375 | return err 376 | } 377 | 378 | return c.mikrotik.ParseResponce(re, v) 379 | } 380 | 381 | // func (c *netinterface) GetByName(name string, v interface{}) error { 382 | // re, err := c.mikrotik.RunArgs(c.path+"/print", "?name="+name) 383 | // if err != nil { 384 | // return err 385 | // } 386 | 387 | // return c.mikrotik.ParseResponce(re, v) 388 | // } 389 | 390 | func (c *netinterface) Set(id string, v interface{}) error { 391 | return c.mikrotik.Set(c.path+"/set", id, v) 392 | } 393 | 394 | func (c *netinterface) Remove(id string) error { 395 | return c.mikrotik.Remove(c.path+"/remove", id) 396 | } 397 | 398 | func (c *netinterface) Enable(id string) error { 399 | return c.mikrotik.Enable(c.path+"/enable", id) 400 | } 401 | 402 | func (c *netinterface) Disable(id string) error { 403 | return c.mikrotik.Disable(c.path+"/disable", id) 404 | } 405 | 406 | func (c *netinterface) Comment(id, comment string) error { 407 | return c.mikrotik.Comment(c.path+"/comment", id, comment) 408 | } 409 | 410 | type wireless struct { 411 | // mikrotik *Mikrotik 412 | // path string 413 | cmd 414 | 415 | SecurityProfiles cmd 416 | } 417 | 418 | func (c *wireless) Scan(name, duration string) (APlist []*WirelessAP, err error) { 419 | re, err := c.mikrotik.RunArgs(c.path+"/scan", "=.id="+name, "=duration="+duration) 420 | if err != nil { 421 | return nil, err 422 | } 423 | 424 | var list []*WirelessAP 425 | err = c.mikrotik.ParseResponce(re, &list) 426 | if err != nil { 427 | return list, err 428 | } 429 | 430 | // remove duplicates 431 | for i := range list { 432 | if c.contains(APlist, list[i]) { 433 | continue 434 | } 435 | APlist = append(APlist, list[i]) 436 | } 437 | 438 | return 439 | } 440 | 441 | func (c *wireless) contains(list []*WirelessAP, ap *WirelessAP) bool { 442 | for i := range list { 443 | if list[i].Address == ap.Address && list[i].SSID == ap.SSID { 444 | return true 445 | } 446 | } 447 | return false 448 | } 449 | 450 | type lte struct { 451 | mikrotik *Mikrotik 452 | path string 453 | } 454 | 455 | func (l *lte) Set(id string, v interface{}) error { 456 | path := l.path 457 | return l.mikrotik.Set(path+"/set", id, v) 458 | } 459 | 460 | func (l *lte) InfoOnce(id string, lteInfo *LteInfo) error { 461 | res, err := l.mikrotik.RunArgs(l.path+"/info", "=.id="+id, "=once=") 462 | if err != nil { 463 | return err 464 | } 465 | 466 | err = l.mikrotik.ParseResponce(res, lteInfo) 467 | if err != nil { 468 | return err 469 | } 470 | 471 | return nil 472 | } 473 | 474 | func (l *lte) List(v interface{}) error { 475 | return l.mikrotik.Print(l.path+"/print", v) 476 | } 477 | 478 | type ethernet struct { 479 | mikrotik *Mikrotik 480 | path string 481 | } 482 | 483 | // func (e *ethernet) Name() error { 484 | // var resp struct{ Name string } 485 | // err := poe.mikrotik.Print(poe.path+"/print", &resp) 486 | 487 | // return resp.Name, err 488 | // } 489 | 490 | // func (l *ethernet) PoEOutAutoOn() error { 491 | // res, err := l.mikrotik.RunArgs(l.path+"/poe-out", "=.id="+id, "=once=") 492 | // if err != nil { 493 | // return err 494 | // } 495 | 496 | // err = l.mikrotik.ParseResponce(res, lteInfo) 497 | // if err != nil { 498 | // return err 499 | // } 500 | 501 | // return nil 502 | // } 503 | 504 | // func (l *ethernet) List(v interface{}) error { 505 | // return l.mikrotik.Print(l.path+"/print", v) 506 | // } 507 | 508 | type ppp struct { 509 | AAA cfg 510 | L2tpSecret cmd 511 | Profile cmd 512 | Secret cmd 513 | } 514 | -------------------------------------------------------------------------------- /mikrotik_test.go: -------------------------------------------------------------------------------- 1 | package mikrotik 2 | 3 | import ( 4 | "testing" 5 | 6 | log "github.com/sirupsen/logrus" 7 | ) 8 | 9 | var mikrotik *Mikrotik 10 | 11 | func init() { 12 | log.SetFlags(log.Lshortfile) 13 | 14 | addr := "192.168.107.78:8728" 15 | user := "admin" 16 | pass := "" 17 | 18 | var err error 19 | mikrotik, err = DialTimeout(addr, user, pass, 3e9) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | } 24 | 25 | func TestRun(t *testing.T) { 26 | _, err := mikrotik.Run("/system/identity/print") 27 | if err != nil { 28 | t.Error(err) 29 | } 30 | 31 | // t.Log(re) 32 | } 33 | 34 | func TestIPAddress(t *testing.T) { 35 | ip := IPAddress{ 36 | Address: "10.0.0.3/24", 37 | Interface: "bridge1", 38 | } 39 | 40 | var list []*IPAddress 41 | if err := mikrotik.IP.Address.List(&list); err != nil { 42 | t.Error(err) 43 | } 44 | 45 | // for _, ip := range list { 46 | // t.Log(ip) 47 | // } 48 | 49 | if err := mikrotik.IP.Address.Add(&ip); err != nil { 50 | t.Error(err) 51 | } 52 | 53 | // t.Log(ip) 54 | 55 | if err := mikrotik.IP.Address.Remove(ip.ID); err != nil { 56 | t.Error(err) 57 | } 58 | } 59 | 60 | func TestSystem(t *testing.T) { 61 | name, err := mikrotik.System.Identity.Name() 62 | if err != nil { 63 | t.Error(err) 64 | t.Fail() 65 | } 66 | t.Logf("/system/idenitity name: %s", name) 67 | 68 | if err := mikrotik.System.Identity.SetName(name); err != nil { 69 | t.Error(err) 70 | t.Fail() 71 | } 72 | } 73 | 74 | func TestInterface(t *testing.T) { 75 | var list []*Interface 76 | if err := mikrotik.Interface.List(&list); err != nil { 77 | t.Error(err) 78 | } 79 | 80 | // for _, intf := range list { 81 | // t.Log(intf) 82 | // } 83 | } 84 | 85 | func TestWirelessInterface(t *testing.T) { 86 | var list []*WirelessInterface 87 | err := mikrotik.Interface.Wireless.List(&list) 88 | if err != nil { 89 | t.Error(err) 90 | } 91 | 92 | for _, intf := range list { 93 | t.Log(intf) 94 | } 95 | } 96 | 97 | func TestWirelessScan(t *testing.T) { 98 | list, err := mikrotik.Interface.Wireless.Scan("wlan1", "5s") 99 | if err != nil { 100 | t.Error(err) 101 | } 102 | 103 | for _, ap := range list { 104 | t.Log(ap) 105 | } 106 | } 107 | 108 | func TestWirelessSecurityProfiles(t *testing.T) { 109 | var list []*WirelessSecurityProfile 110 | err := mikrotik.Interface.Wireless.SecurityProfiles.List(&list) 111 | if err != nil { 112 | t.Error(err) 113 | } 114 | 115 | for _, item := range list { 116 | t.Logf("%+v", item) 117 | } 118 | 119 | var profile *WirelessSecurityProfile 120 | err = mikrotik.Interface.Wireless.SecurityProfiles.Find("?default=true", &profile) 121 | if err != nil { 122 | t.Error(err) 123 | t.FailNow() 124 | } 125 | 126 | err = mikrotik.Interface.Wireless.SecurityProfiles.Set(profile.ID, WirelessSecurityProfile{Wpa2PreSharedKey: "12345678"}) 127 | if err != nil { 128 | t.Error(err) 129 | } 130 | } 131 | 132 | func TestFind(t *testing.T) { 133 | var list []*Interface 134 | if err := mikrotik.Interface.Find("type=ether", &list); err != nil { 135 | t.Error(err) 136 | } 137 | 138 | if len(list) == 0 { 139 | t.Error("failed find items") 140 | } 141 | 142 | // for _, intf := range list { 143 | // t.Log(intf) 144 | // } 145 | 146 | var intf *Interface 147 | if err := mikrotik.Interface.Find("name=ether1", &intf); err != nil { 148 | t.Error(err) 149 | } 150 | t.Log(intf) 151 | 152 | var ipaddrs []*IPAddress 153 | err := mikrotik.IP.Address.Find(`interface=ppp-out51`, &ipaddrs) 154 | if err != nil { 155 | t.Error(err) 156 | } 157 | if len(ipaddrs) == 0 { 158 | t.Error("ipaddresses not found") 159 | } 160 | for _, ipa := range ipaddrs { 161 | t.Log(ipa) 162 | } 163 | } 164 | 165 | // func TestGet(t *testing.T) { 166 | // var intf *Interface 167 | // if err := mikrotik.Interface.GetByName("ether1", &intf); err != nil { 168 | // t.Error(err) 169 | // } 170 | 171 | // t.Log(intf) 172 | // } 173 | 174 | func TestRoute(t *testing.T) { 175 | var list []*Route 176 | if err := mikrotik.IP.Route.List(&list); err != nil { 177 | t.Error(err) 178 | } 179 | 180 | for _, item := range list { 181 | t.Log(item) 182 | } 183 | 184 | if len(list) == 0 { 185 | t.Error("routes not found") 186 | } 187 | 188 | route := list[0] 189 | err := mikrotik.IP.Route.Set(route.ID, &Route{Comment: "newcomment"}) 190 | if err != nil { 191 | t.Error(err) 192 | } 193 | 194 | // err = mikrotik.IP.Route.Set(route.ID, Route{Distance: route.Distance}) 195 | // if err != nil { 196 | // t.Error(err) 197 | // } 198 | } 199 | 200 | func TestFirewallNAT(t *testing.T) { 201 | // mikrotik.Debug(true) 202 | 203 | var rules []NATRule 204 | if err := mikrotik.IP.Firewall.NAT.List(&rules); err != nil { 205 | t.Error(err) 206 | } 207 | 208 | // for _, rule := range rules { 209 | // t.Logf("%+v", rule) 210 | // } 211 | 212 | rule := NATRule{ 213 | Chain: ChainDstNAT, 214 | Protocol: "tcp", 215 | DstPort: 2222, 216 | Action: FirewallActionNetmap, 217 | ToAddresses: "192.168.241.1", 218 | ToPorts: "22", 219 | } 220 | if err := mikrotik.IP.Firewall.NAT.Add(&rule); err != nil { 221 | t.Error(err) 222 | } 223 | } 224 | 225 | func TestSSTPServer(t *testing.T) { 226 | s := SSTPserver{ 227 | Name: "test-sstp-server", 228 | User: "test-sstp-user", 229 | } 230 | err := mikrotik.Interface.SSTPServer.Add(&s) 231 | if err != nil { 232 | t.Error(err) 233 | } 234 | 235 | t.Logf("%+v", s) 236 | 237 | var s2 SSTPserver 238 | if err = mikrotik.Interface.SSTPServer.Find(".id="+s.ID, &s2); err != nil { 239 | t.Error(err) 240 | } 241 | 242 | t.Logf("%+v", s2) 243 | 244 | if err = mikrotik.Interface.SSTPServer.Remove(s.ID); err != nil { 245 | t.Error(err) 246 | } 247 | } 248 | 249 | func TestPPPprofile(t *testing.T) { 250 | var profiles []*PPPprofile 251 | if err := mikrotik.PPP.Profile.List(&profiles); err != nil { 252 | t.Error(err) 253 | } 254 | 255 | for _, p := range profiles { 256 | t.Logf("%+v", p) 257 | } 258 | } 259 | 260 | func TestPPPsecret(t *testing.T) { 261 | secret := Secret{ 262 | Name: "test-secret", 263 | Password: "test-password", 264 | Service: PPPServiceAny, 265 | Profile: "default", 266 | } 267 | if err := mikrotik.PPP.Secret.Add(&secret); err != nil { 268 | t.Error(err) 269 | } 270 | 271 | secret.Profile = "default-encryption" 272 | if err := mikrotik.PPP.Secret.Set(secret.ID, &secret); err != nil { 273 | t.Error(err) 274 | } 275 | 276 | var secrets []*Secret 277 | if err := mikrotik.PPP.Secret.List(&secrets); err != nil { 278 | t.Error(err) 279 | } 280 | 281 | for _, p := range secrets { 282 | t.Logf("%+v", p) 283 | } 284 | 285 | if err := mikrotik.PPP.Secret.Remove(secret.ID); err != nil { 286 | t.Error(err) 287 | } 288 | } 289 | 290 | func TestSSTPClient(t *testing.T) { 291 | c := SSTPclient{ 292 | Name: "test", 293 | ConnectTo: "10.0.0.1:443", 294 | User: "username", 295 | Password: "password", 296 | } 297 | 298 | if err := mikrotik.Interface.SSTPClient.Add(&c); err != nil { 299 | t.Error(err) 300 | // t.FailNow() 301 | } 302 | 303 | if err := mikrotik.Interface.SSTPClient.Find("name=test", &c); err != nil { 304 | t.Error(err) 305 | t.FailNow() 306 | } 307 | 308 | t.Log(c.ID) 309 | 310 | if err := mikrotik.Interface.SSTPClient.Remove(c.ID); err != nil { 311 | t.Error(err) 312 | t.FailNow() 313 | } 314 | } 315 | 316 | func TestPing(t *testing.T) { 317 | resp, err := mikrotik.Ping("8.8.8.8", 1).Start() 318 | if err != nil { 319 | t.Error(err) 320 | } 321 | 322 | for _, r := range resp { 323 | t.Logf("%+v", r) 324 | } 325 | 326 | ping := mikrotik.Ping("8.8.8.8", 1) 327 | ping.SrcAddress = "192.168.240.1" 328 | resp, err = ping.Start() 329 | if err != nil { 330 | t.Error(err) 331 | } 332 | 333 | for _, r := range resp { 334 | t.Logf("%+v", r) 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | package mikrotik 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "time" 7 | ) 8 | 9 | // ==================================== 10 | // 11 | // Entities 12 | // 13 | // ==================================== 14 | 15 | type Ping struct { 16 | mikrotik *Mikrotik 17 | Address string 18 | 19 | Count int 20 | Interface string 21 | Interval int 22 | RoutingTable string 23 | Size int 24 | SrcAddress string 25 | TTL int `mikrotik:"ttl"` 26 | } 27 | 28 | type PingResponse struct { 29 | Host string 30 | 31 | Status string 32 | 33 | Sent int 34 | Received int 35 | PacketLoss int 36 | 37 | TTL int `mikrotik:"ttl"` 38 | Seq int 39 | Size int 40 | 41 | Time time.Duration 42 | MinRTT time.Duration `mikrotik:"min-rtt"` 43 | AvgRTT time.Duration `mikrotik:"avg-rtt"` 44 | MaxRTT time.Duration `mikrotik:"max-rtt"` 45 | } 46 | 47 | // IPAddress /ip/address 48 | type IPAddress struct { 49 | ID string `mikrotik:".id"` 50 | 51 | Address string 52 | Network string 53 | 54 | Interface string 55 | ActualInterface string 56 | 57 | Invalid bool 58 | Dynamic bool 59 | Disabled bool 60 | } 61 | 62 | func (ipa IPAddress) String() string { 63 | return fmt.Sprintf("%s dev %s", ipa.Address, ipa.Interface) 64 | } 65 | 66 | // Route /ip/route 67 | type Route struct { 68 | ID string `mikrotik:".id"` 69 | 70 | DstAddress string 71 | PrefSrc string 72 | Gateway string 73 | GatewayStatus string 74 | 75 | Distance int 76 | Scope int 77 | TargetScope int 78 | 79 | Active bool 80 | Dynamic bool 81 | Static bool 82 | Disabled bool 83 | 84 | Comment string 85 | } 86 | 87 | func (r Route) String() string { 88 | return fmt.Sprintf("%s %s %s %d %s", r.DstAddress, r.PrefSrc, r.Gateway, r.Distance, r.Comment) 89 | } 90 | 91 | // NATRule /ip/firewall/nat 92 | type NATRule struct { 93 | ID string `mikrotik:".id"` 94 | 95 | Chain string 96 | Action string 97 | 98 | Protocol string 99 | SrcAddress net.IP 100 | DstAddress net.IP 101 | ToAddresses string 102 | ToPorts string 103 | 104 | InInterface string 105 | OutInterface string 106 | 107 | SrcPort int 108 | DstPort int 109 | Port int 110 | 111 | PacketMark string 112 | ConnectionMark string 113 | RoutingMark string 114 | RoutingTable string 115 | 116 | ConnectionType string 117 | 118 | Log bool 119 | LogPrefix string 120 | 121 | Bytes int 122 | Packets int 123 | Invalid bool 124 | Dynamic bool 125 | Disabled bool 126 | 127 | Comment string 128 | } 129 | 130 | const ( 131 | ChainSrcNAT = "srcnat" 132 | ChainDstNAT = "dstnat" 133 | ) 134 | 135 | const ( 136 | FirewallActionAccept = "accept" 137 | FirewallActionAddDstToAddressList = "add-dst-to-address-list" 138 | FirewallActionDstNAT = "dst-nat" 139 | FirewallActionJump = "jump" 140 | FirewallActionLog = "log" 141 | FirewallActionMasquerade = "masquerade" 142 | FirewallActionNetmap = "netmap" 143 | FirewallActionPassthrough = "passthrough" 144 | FirewallActionRedirect = "redirect" 145 | FirewallActionReturn = "return" 146 | FirewallActionSame = "same" 147 | FirewallActionSrcNAT = "src-nat" 148 | ) 149 | 150 | // MangleRule /ip/firewall/mangle 151 | type MangleRule struct { 152 | ID string `mikrotik:".id"` 153 | 154 | Action string 155 | Chain string 156 | 157 | NewRoutingMark string 158 | NewConnectionMark string 159 | NewPacketMark string 160 | 161 | ConnectionMark string 162 | PacketMark string 163 | 164 | Passthrough bool 165 | ConnectionState string 166 | DstAddressType string 167 | 168 | InInterface string 169 | Nth string 170 | 171 | Log bool 172 | LogPrefix string 173 | 174 | Bytes int 175 | Packets int 176 | 177 | Invalid bool 178 | Dynamic bool 179 | Disabled bool 180 | 181 | Comment string 182 | } 183 | 184 | type SystemNTPClient struct { 185 | Enabled string 186 | ServerDNSNames string `mikrotik:"server-dns-names"` 187 | Mode string 188 | } 189 | 190 | type Interface struct { 191 | ID string `mikrotik:".id"` 192 | Name string 193 | DefaultName string 194 | Type string 195 | MTU string `mikrotik:"mtu"` 196 | ActualMTU string `mikrotik:"actual-mtu"` 197 | L2MTU string `mikrotik:"l2mtu"` 198 | MaxL2MTU string `mikrotik:"max-l2mtu"` 199 | MACAddress string `mikrotik:"mac-address"` 200 | FastPath bool 201 | LinkDowns int 202 | RxByte int 203 | TxByte int 204 | RxPacket int 205 | TxPacket int 206 | RxDrop int 207 | TxDrop int 208 | RxError int 209 | TxError int 210 | FpRxByte int 211 | FpTxByte int 212 | FpRxPacket int 213 | FpTxPacket int 214 | 215 | Running bool 216 | Slave bool 217 | Disabled bool 218 | Comment string 219 | } 220 | 221 | type WirelessInterface struct { 222 | ID string `mikrotik:".id"` 223 | DefaultName string `mikrotik:"default-name"` 224 | Name string `mikrotik:"name"` 225 | Mtu int `mikrotik:"mtu"` 226 | L2mtu int `mikrotik:"l2mtu"` 227 | MacCddress string `mikrotik:"mac-address"` 228 | Arp string `mikrotik:"arp"` 229 | ArpTimeout string `mikrotik:"arp-timeout"` 230 | DisableRunningCheck string `mikrotik:"disable-running-check"` 231 | InterfaceType string `mikrotik:"interface-type"` 232 | RadioName string `mikrotik:"radio-name"` 233 | Mode string `mikrotik:"mode"` 234 | SSID string `mikrotik:"ssid"` 235 | Area string `mikrotik:"area"` 236 | FrequencyMode string `mikrotik:"frequency-mode"` 237 | Country string `mikrotik:"country"` 238 | AntennaGain int `mikrotik:"antenna-gain"` 239 | Frequency int `mikrotik:"frequency"` 240 | Band string `mikrotik:"band"` 241 | ChannelWidth string `mikrotik:"channel-width"` 242 | ScanList string `mikrotik:"scan-list"` 243 | WirelessProtocol string `mikrotik:"wireless-protocol"` 244 | RateSet string `mikrotik:"rate-set"` 245 | SupportedRatesB string `mikrotik:"supported-rates-b"` 246 | SupportedRatesAG string `mikrotik:"supported-rates-a/g"` 247 | BasicRatesB string `mikrotik:"basic-rates-b"` 248 | BasicRatesAG string `mikrotik:"basic-rates-a/g"` 249 | MaxStationCount int `mikrotik:"max-station-count"` 250 | Distance string `mikrotik:"distance"` 251 | TxPowerMode string `mikrotik:"tx-power-mode"` 252 | NoiseFloorThreshold string `mikrotik:"noise-floor-threshold"` 253 | Nv2NoiseFloorOffset string `mikrotik:"nv2-noise-floor-offset"` 254 | VlanMode string `mikrotik:"vlan-mode"` 255 | VlanID string `mikrotik:"vlan-id"` 256 | WDSmode string `mikrotik:"wds-mode"` 257 | WDSdefaultBridge string `mikrotik:"wds-default-bridge"` 258 | WDSdefaultCost int `mikrotik:"wds-default-cost"` 259 | WDScostRange string `mikrotik:"wds-cost-range"` 260 | WDSignoreSSID string `mikrotik:"wds-ignore-ssid"` 261 | UpdateStatsInterval string `mikrotik:"update-stats-interval"` 262 | BridgeMode string `mikrotik:"bridge-mode"` 263 | DefaultAuthentication bool `mikrotik:"default-authentication"` 264 | DefaultForwarding bool `mikrotik:"default-forwarding"` 265 | DefaultAPTxLimit int `mikrotik:"default-ap-tx-limit"` 266 | DefaultClientTxLimit int `mikrotik:"default-client-tx-limit"` 267 | WMMsupport string `mikrotik:"wmm-support"` 268 | HideSSID bool `mikrotik:"hide-ssid"` 269 | SecurityProfile string `mikrotik:"security-profile"` 270 | InterworkingProfile string `mikrotik:"interworking-profile"` 271 | WPSmode string `mikrotik:"wps-mode"` 272 | StationRoaming string `mikrotik:"station-roaming"` 273 | DisconnectTimeout string `mikrotik:"disconnect-timeout"` 274 | OnFailRetryTime string `mikrotik:"on-fail-retry-time"` 275 | PreambleMode string `mikrotik:"preamble-mode"` 276 | Compression string `mikrotik:"compression"` 277 | AllowSharedkey string `mikrotik:"allow-sharedkey"` 278 | StationBridgeCloneMac string `mikrotik:"station-bridge-clone-mac"` 279 | AmpduPriorities string `mikrotik:"ampdu-priorities"` 280 | GuardInterval string `mikrotik:"guard-interval"` 281 | HtSupportedMCS string `mikrotik:"ht-supported-mcs"` 282 | HtBasicMCS string `mikrotik:"ht-basic-mcs"` 283 | TxChains string `mikrotik:"tx-chains"` 284 | RxChains string `mikrotik:"rx-chains"` 285 | AmsduLimit int `mikrotik:"amsdu-limit"` 286 | AmsduThreshold int `mikrotik:"amsdu-threshold"` 287 | TdmaPeriodSize int `mikrotik:"tdma-period-size"` 288 | Nv2QueueCount int `mikrotik:"nv2-queue-count"` 289 | Nv2QOS string `mikrotik:"nv2-qos"` 290 | Nv2CellRadius int `mikrotik:"nv2-cell-radius"` 291 | Nv2Security string `mikrotik:"nv2-security"` 292 | Nv2PresharedKey string `mikrotik:"nv2-preshared-key"` 293 | HwRetries string `mikrotik:"hw-retries"` 294 | FrameLifetime string `mikrotik:"frame-lifetime"` 295 | AdaptiveNoiseImmunity string `mikrotik:"adaptive-noise-immunity"` 296 | HwFragmentationThreshold string `mikrotik:"hw-fragmentation-threshold"` 297 | HwProtectionMode string `mikrotik:"hw-protection-mode"` 298 | HwProtectionThreshold string `mikrotik:"hw-protection-threshold"` 299 | FrequencyOffset string `mikrotik:"frequency-offset"` 300 | RateSelection string `mikrotik:"rate-selection"` 301 | MulticastHelper string `mikrotik:"multicast-helper"` 302 | MulticastBuffering string `mikrotik:"multicast-buffering"` 303 | KeepaliveFrames string `mikrotik:"keepalive-frames"` 304 | Running bool `mikrotik:"running"` 305 | Disabled bool `mikrotik:"disabled"` 306 | } 307 | 308 | type WirelessAP struct { 309 | Address string 310 | SSID string `mikrotik:"ssid"` 311 | Channel string 312 | SIG int `mikrotik:"sig"` 313 | NF int `mikrotik:"nf"` 314 | SNR int `mikrotik:"snr"` 315 | RadioName string `mikrotik:"radio-name"` 316 | RouterOSVersion string `mikrotik:"routeros-version"` 317 | Section int 318 | } 319 | 320 | type WirelessSecurityProfile struct { 321 | ID string `mikrotik:".id"` 322 | Name string 323 | Mode string 324 | Default bool 325 | 326 | AuthenticationTypes string `mikrotik:"authentication-types"` 327 | UnicastCiphers string `mikrotik:"unicast-ciphers"` 328 | GroupCiphers string `mikrotik:"group-ciphers"` 329 | 330 | WpaPreSharedKey string `mikrotik:"wpa-pre-shared-key"` 331 | Wpa2PreSharedKey string `mikrotik:"wpa2-pre-shared-key"` 332 | SupplicantIdentity string `mikrotik:"supplicant-identity"` 333 | EAPmethods string `mikrotik:"eap-methods"` 334 | 335 | TLSmode string `mikrotik:"tls-mode"` 336 | TLScertificate string `mikrotik:"tls-certificate"` 337 | 338 | Mschapv2username string `mikrotik:"mschapv2-username"` 339 | Mschapv2password string `mikrotik:"mschapv2-password"` 340 | 341 | RadiusMACauthentication string `mikrotik:"radius-mac-authentication"` 342 | RadiusMACaccounting string `mikrotik:"radius-mac-accounting"` 343 | RadiusEAPaccounting string `mikrotik:"radius-eap-accounting"` 344 | RadiusMACformat string `mikrotik:"radius-mac-format"` 345 | RadiusMACmode string `mikrotik:"radius-mac-mode"` 346 | RadiusMACcaching string `mikrotik:"radius-mac-caching"` 347 | InterimUpdate string `mikrotik:"interim-update"` 348 | GroupKeyUpdate string `mikrotik:"group-key-update"` 349 | 350 | ManagementProtection string `mikrotik:"management-protection"` 351 | ManagementProtectionKey string `mikrotik:"management-protection-key"` 352 | } 353 | 354 | const ( 355 | WPA_PSK = "wpa-psk" 356 | WPA2_PSK = "wpa2-psk" 357 | WPA_EAP = "wpa-eap" 358 | WPA2_EAP = "wpa2-eap" 359 | ) 360 | 361 | const ( 362 | WirelessSecurityMode_None = "none" 363 | WirelessSecurityMode_DynamicKeys = "dynamic-keys" 364 | WirelessSecurityMode_StaticKeysRequired = "static-keys-required" 365 | WirelessSecurityMode_StaticKeysOptional = "static-keys-optional" 366 | ) 367 | 368 | type SSTPserver struct { 369 | ID string `mikrotik:".id"` 370 | Name string 371 | User string 372 | 373 | Running bool 374 | Disabled bool 375 | } 376 | 377 | type SSTPclient struct { 378 | ID string `mikrotik:".id"` 379 | Name string 380 | ConnectTo string `mikrotik:"connect-to"` 381 | Certificate string 382 | VerifyServerCertificate string `mikrotik:"verify-server-certificate"` 383 | VerifyServerAddressFromCertificate string `mikrotik:"verify-server-address-from-certificate"` 384 | User string 385 | Password string 386 | Profile string 387 | AddDefaultRoute string `mikrotik:"add-default-route"` 388 | DefaultRouteDistance int `mikrotik:"default-route-distance"` 389 | DialOnDemand bool 390 | Running bool 391 | Disabled bool 392 | } 393 | 394 | type PPPprofile struct { 395 | ID string `mikrotik:".id"` 396 | Name string 397 | UseMPLS string `mikrotik:"use-mpls"` 398 | UseCompression string `mikrotik:"use-compression"` 399 | UseEncryption string `mikrotik:"use-encryption"` 400 | OnlyOne string `mikrotik:"only-one"` 401 | ChangeTCPMSS string `mikrotik:"change-tcp-mss"` 402 | UseUPNP string `mikrotik:"use-upnp"` 403 | AddressList string `mikrotik:"address-list"` 404 | OnUp string `mikrotik:"on-up"` 405 | OnDown string `mikrotik:"on-down"` 406 | Default bool `mikrotik:"default"` 407 | } 408 | 409 | type Secret struct { 410 | ID string `mikrotik:".id"` 411 | Name string 412 | Password string 413 | Service string 414 | CallerID string `mikrotik:"caller-id"` 415 | Profile string 416 | LocalAddress net.IP `mikrotik:"local-address"` 417 | RemoteAddress net.IP `mikrotik:"remote-address"` 418 | Routes string 419 | LimitBytesIn int `mikrotik:"limit-bytes-in"` 420 | LimitBytesOut int `mikrotik:"limit-bytes-out"` 421 | LastLoggedOut string `mikrotik:"last-logged-out,ro"` 422 | Disabled bool 423 | Comment string 424 | } 425 | 426 | const ( 427 | PPPServiceAny = "any" 428 | PPPServiceAsync = "async" 429 | PPPServiceL2TP = "l2tp" 430 | PPPServiceOVPN = "ovpn" 431 | PPPServicePPPoE = "pppoe" 432 | PPPServicePPTP = "pptp" 433 | PPPServiceSSTP = "sstp" 434 | ) 435 | 436 | // Routerboard - information from /system/routerboard/print 437 | type Routerboard struct { 438 | Routerboard bool 439 | BoardName string `mikrotik:"board-name"` 440 | Model string 441 | SerialNumber string `mikrotik:"serial-number"` 442 | FirmwareType string `mikrotik:"firmware-type"` 443 | FactoryFirmware string `mikrotik:"factory-firmware"` 444 | CurrentFirmware string `mikrotik:"current-firmware"` 445 | UpgradeFirmware string `mikrotik:"upgrade-firmware"` 446 | } 447 | 448 | type LteInfo struct { 449 | // this data was sent without pin 450 | PinStatus string `mikrotik:"pin-status"` 451 | Functionality string 452 | Manufacturer string 453 | Model string 454 | Revision string 455 | IMEI string `mikrotik:"imei"` 456 | 457 | // this data was empty withut pin and was sent with correct pin set. 458 | RegistrationStatus string `mikrotik:"registration-status"` 459 | CurrentOperator string `mikrotik:"current-operator"` 460 | Lac string 461 | CurrentCellID string `mikrotik:"current-cellid"` 462 | EnbID string `mikrotik:"enb-id"` 463 | SectorID string `mikrotik:"sector-id"` 464 | PhyCellID string `mikrotik:"phy-cellid"` 465 | AccessTechnology string `mikrotik:"access-technology"` 466 | SessionUptime string `mikrotik:"session-uptime"` 467 | IMSI string `mikrotik:"imsi"` 468 | UICC string `mikrotik:"uicc"` 469 | SubscriberNumber string `mikrotik:"subscriber-number"` 470 | Earfcn string 471 | Rssi string // should be float 472 | Rsrp string // should be float 473 | Rsrq string // should be float 474 | Sinr string // should be float 475 | } 476 | 477 | type LtePrint struct { 478 | ID string `mikrotik:".id"` 479 | Name string 480 | Mtu int 481 | MacAddress string `mikrotik:"mac-address"` 482 | Pin string 483 | ApnProfiles string `mikrotik:"apn-profiles"` 484 | AllowRoaming bool `mikrotik:"allow-roaming"` 485 | NetworkMode string `mikrotik:"network-mode"` 486 | Running bool 487 | Disalbed bool 488 | } 489 | 490 | // Resource from `/system resource print` 491 | type Resource struct { 492 | Uptime string `mikrotik:"uptime"` 493 | BuildTime string `mikrotik:"build-time"` 494 | FreeMemory int `mikrotik:"free-memory"` 495 | TotalMemory int `mikrotik:"total-memory"` 496 | CPU string `mikrotik:"cpu"` 497 | CPUCount int `mikrotik:"cpu-count"` 498 | CPUFrequency int `mikrotik:"cpu-frequency"` // MHZ 499 | CPULoad int `mikrotik:"cpu-load"` // % 500 | FreeHddSpace int `mikrotik:"free-hdd-space"` 501 | TotalHddSpace int `mikrotik:"total-hdd-space"` 502 | WriteSectSinceReboot int `mikrotik:"write-sect-since-reboot"` 503 | WriteSectTotal int `mikrotik:"write-sect-total"` 504 | BadBlocks string `mikrotik:"bad-blocks"` // % 505 | ArchitectureName string `mikrotik:"architecture-name"` 506 | BoardName string `mikrotik:"board-name"` 507 | Platform string `mikrotik:"platform"` 508 | } 509 | 510 | type Ethernet struct { 511 | // TODO: add other fields 512 | PoEOut string `mikrotik:"poe-out"` // auto-on, forced-on, off 513 | } 514 | -------------------------------------------------------------------------------- /values.go: -------------------------------------------------------------------------------- 1 | package mikrotik 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | "time" 10 | "unicode" 11 | ) 12 | 13 | type Values struct { 14 | values map[string]string 15 | } 16 | 17 | func ValuesFrom(m map[string]string) Values { 18 | return Values{m} 19 | } 20 | 21 | func (v Values) Set(key, val string) { 22 | if v.values == nil { 23 | v.values = make(map[string]string) 24 | } 25 | 26 | v.values[key] = val 27 | } 28 | 29 | func (v Values) Lookup(key string) (string, bool) { 30 | if v.values == nil { 31 | return "", false 32 | } 33 | 34 | val, ok := v.values[key] 35 | return val, ok 36 | } 37 | 38 | func (v Values) Get(key string) string { 39 | if v.values == nil { 40 | return "" 41 | } 42 | 43 | if val, ok := v.values[key]; ok { 44 | return val 45 | } 46 | 47 | return "" 48 | } 49 | 50 | func (v Values) Del(key string) { 51 | if v.values == nil { 52 | return 53 | } 54 | 55 | delete(v.values, key) 56 | } 57 | 58 | func (v Values) String() string { 59 | if v.values == nil { 60 | return "" 61 | } 62 | 63 | var vals []string 64 | for key, val := range v.values { 65 | vals = append(vals, fmt.Sprintf("%s=%s", key, val)) 66 | } 67 | 68 | return strings.Join(vals, " ") 69 | } 70 | 71 | func (v Values) To(i interface{}) (err error) { 72 | rv := reflect.ValueOf(i).Elem() 73 | 74 | //if it`s i not a slice 75 | if rv.Kind() != reflect.Slice { 76 | 77 | itemType := reflect.TypeOf(i) 78 | for itemType.Kind() == reflect.Ptr { 79 | itemType = itemType.Elem() 80 | } 81 | item := reflect.New(itemType) 82 | 83 | if err := v.setStruct(item.Elem(), itemType); err != nil { 84 | return err 85 | } 86 | 87 | if rv.Kind() != reflect.Ptr && item.Kind() == reflect.Ptr { 88 | rv.Set(item.Elem()) 89 | } else { 90 | rv.Set(item) 91 | } 92 | return 93 | } 94 | 95 | if rv.Kind() == reflect.Ptr { 96 | rv = rv.Elem() 97 | } 98 | 99 | itemType := rv.Type().Elem() 100 | 101 | //if slice element is pointer on struct 102 | if itemType.Kind() == reflect.Ptr { 103 | itemType = itemType.Elem() 104 | item := reflect.New(itemType) 105 | if err := v.setStruct(item.Elem(), itemType); err != nil { 106 | return err 107 | } 108 | 109 | rv.Set(reflect.Append(rv, item)) 110 | } else { 111 | //if slice element is struct 112 | item := reflect.New(itemType) 113 | if err := v.setStruct(item.Elem(), itemType); err != nil { 114 | return err 115 | } 116 | 117 | rv.Set(reflect.Append(rv, item.Elem())) 118 | } 119 | 120 | return 121 | } 122 | 123 | func (v Values) setStruct(rv reflect.Value, rt reflect.Type) (err error) { 124 | for i := 0; i < rv.NumField(); i++ { 125 | vfield := rv.Field(i) 126 | tfield := rt.Field(i) 127 | if !vfield.IsValid() && !vfield.CanSet() { 128 | continue 129 | } 130 | 131 | var fieldname string 132 | 133 | if tag, ok := tfield.Tag.Lookup("mikrotik"); ok { 134 | fieldname = tag 135 | } else { 136 | fieldname = ToMikrotikName(tfield.Name) 137 | } 138 | 139 | // log.Println(fieldname) 140 | val, ok := v.Lookup(fieldname) 141 | if !ok { 142 | continue 143 | } 144 | 145 | if tag, ok := tfield.Tag.Lookup("trim"); ok { 146 | val = strings.Trim(val, tag) 147 | } 148 | 149 | switch vfield.Interface().(type) { 150 | case bool: 151 | b, err := strconv.ParseBool(val) 152 | if err != nil { 153 | return err 154 | } 155 | vfield.SetBool(b) 156 | 157 | case int: 158 | n, err := strconv.Atoi(val) 159 | if err != nil { 160 | return err 161 | } 162 | vfield.SetInt(int64(n)) 163 | 164 | case net.IP: 165 | ip := net.ParseIP(val) 166 | vfield.Set(reflect.ValueOf(ip)) 167 | 168 | case time.Duration: 169 | dur, err := time.ParseDuration(val) 170 | if err != nil { 171 | return err 172 | } 173 | vfield.Set(reflect.ValueOf(dur)) 174 | 175 | default: 176 | fieldVal := reflect.ValueOf(val).Convert(vfield.Type()) 177 | vfield.Set(fieldVal) 178 | } 179 | } 180 | 181 | return nil 182 | } 183 | 184 | //ToFieldName convert incoming fieldname to struct fieldname, example: 185 | // address -> Address 186 | // actual-interface -> ActualInterface 187 | func ToFieldName(fieldname string) string { 188 | var newname strings.Builder 189 | newname.Grow(len(fieldname)) 190 | 191 | for _, s := range strings.Split(fieldname, "-") { 192 | newname.WriteString(strings.Title(s)) 193 | } 194 | 195 | return newname.String() 196 | } 197 | 198 | //ToMikrotikName convert struct fieldname to mikrotik like: 199 | // Address -> address 200 | // ActualInterface -> actual-interface 201 | func ToMikrotikName(fieldname string) string { 202 | var newname strings.Builder 203 | newname.Grow(len(fieldname) + 2) 204 | 205 | for i, c := range fieldname { 206 | if i > 0 && unicode.IsUpper(c) { 207 | newname.WriteString("-") 208 | } 209 | newname.WriteRune(unicode.ToLower(c)) 210 | 211 | } 212 | 213 | return newname.String() 214 | } 215 | 216 | func ToArgs(i interface{}) (args []string) { 217 | rv := reflect.ValueOf(i) 218 | for rv.Kind() == reflect.Ptr { 219 | rv = rv.Elem() 220 | } 221 | rt := rv.Type() 222 | 223 | if rv.Kind() == reflect.String { 224 | return strings.Split(rv.String(), " ") 225 | } 226 | 227 | for i := 0; i < rv.NumField(); i++ { 228 | field := rv.Field(i) 229 | structField := rt.Field(i) 230 | 231 | // if !field.CanSet() { 232 | // continue 233 | // } 234 | 235 | if IsEmpty(field) { 236 | continue 237 | } 238 | 239 | var name string 240 | tag, ok := structField.Tag.Lookup("mikrotik") 241 | if ok && tag != "" { 242 | if tag == "-" { 243 | continue 244 | } 245 | 246 | tags := strings.Split(tag, ",") 247 | if len(tags) > 1 && tags[1] == "ro" { 248 | continue 249 | } 250 | name = tags[0] 251 | } else { 252 | name = ToMikrotikName(structField.Name) 253 | } 254 | 255 | args = append(args, fmt.Sprintf("=%s=%v", name, field.Interface())) 256 | } 257 | 258 | return 259 | } 260 | 261 | func IsEmpty(v reflect.Value) bool { 262 | switch v.Interface().(type) { 263 | case string: 264 | return v.String() == "" 265 | case bool: 266 | return v.Bool() == false 267 | case int: 268 | return v.Int() == 0 269 | case net.IP: 270 | return v.Len() == 0 271 | } 272 | 273 | return false 274 | } 275 | 276 | func SetID(i interface{}, id string) { 277 | rv := reflect.ValueOf(i) 278 | if rv.Kind() == reflect.Ptr { 279 | rv = rv.Elem() 280 | } 281 | 282 | if !rv.CanSet() { 283 | return 284 | } 285 | 286 | rv.FieldByName("ID").SetString(id) 287 | } 288 | --------------------------------------------------------------------------------