Datasheet

1 2 3 4 5 6 7 8 9 10
PDF generated using the open source mwlib toolkit. See http://code.pediapress.com/ for more information.
PDF generated at: Thu, 19 Dec 2013 20:05:50 CET
Mikrotik - Part 7
User Topics

Summary of content (280 pages)

Results:
    '; foreach ($results as $result) { //Add whatever you want displayed in this section.

  • PAGE 55

    API PHP package 52 //Here's the fun part - actually changing the password $setRequest = new RouterOS\Request('/ip hotspot user set'); $client($setRequest ->setArgument('numbers', $hotspotUsername) ->setArgument('password', $_POST['password']) ); } } ?> PAGE 56

    API PHP package 53

"Forgotten password" form for hotspot users The following script needs to be accessible from a server in a walled garden, i.e. users must not need to be logged in to access it. You should link to it from the login page.

  • PAGE 57

    API PHP package 54 $printRequest->setQuery( RouterOS\Query::where('email', $_POST['email'])->andWhere('comment', $_POST['phone']) ); $id = $client->sendSync($printRequest)->getArgument('.id'); if (null === $id) { $errors[] = 'Email or phone does not match that of any user.'; } } if (!isset($_POST['password']) || !isset($_POST['password2'])) { $errors[] = 'Setting a new password is required.'; } if (empty($errors)) { if ($_POST['password'] !== $_POST['password2']) { $errors[] = 'Passwords do not match.

  • PAGE 58

    API PHP package 55

    • PAGE 59

      API PHP package //Adjust RouterOS IP, username and password accordingly. $client = new RouterOS\Client('192.168.0.1', 'admin', 'password'); $printRequest = new RouterOS\Request('/ip arp print .

    • PAGE 60

      API in C using winsock API in C using winsock This is an implementation of RouterOS API written on C that using winsock2 for compatibility with Windows operation systems. It's based on API in C [1] (You should use all sources from here, except mikrotik-api.c, that incompatible with Windows). Use compatible mikrotik-api.c source from below. If your compiler does not support "#pragma comment", add ws2_32.lib to your linker manually. RouterOS API Source file (mikrotik-api.

    • PAGE 61

      API in C using winsock // Updated 28 August 2011 by hel #include #include #pragma comment(lib, "ws2_32.lib") #include #include #include #include "md5.h" #include "mikrotik-api.

    • PAGE 62

      API in C using winsock } // determine endianness of this machine // iLittleEndian will be set to 1 if we are // on a little endian machine...

    • PAGE 63

      API in C using winsock stReadSentence = readSentence(fdSock); DEBUG ? printSentence (&stReadSentence) : 0; if (stReadSentence.iReturnValue != DONE) { printf("error.\n"); exit(0); } // extract md5 string from the challenge sentence szMD5Challenge = strtok(stReadSentence.

    • PAGE 64

      API in C using winsock 61 DEBUG ? printSentence (&stReadSentence) : 0; if (stReadSentence.

    • PAGE 65

      API in C using winsock else { cEncodedLength[0] = cLength[2] | 0x80; cEncodedLength[1] = cLength[3]; } send (fdSock, cEncodedLength, 2, 0); } // write 3 bytes else if (iLen < 0x200000) { DEBUG ? printf("iLen < 0x200000.

    • PAGE 66

      API in C using winsock cEncodedLength[2] = cLength[2]; cEncodedLength[3] = cLength[3]; } send (fdSock, cEncodedLength, 4, 0); } else // this should never happen { printf("length of word is %d\n", iLen); printf("word is too long.

    • PAGE 67

      API in C using winsock 64 writeWord(fdSock, ""); } /******************************************************************** * Read a message length from the socket * * 80 = 10000000 (2 character encoded length) * C0 = 11000000 (3 character encoded length) * E0 = 11100000 (4 character encoded length) * * Message length is returned ********************************************************************/ int readLen(int fdSock) { char cFirstChar; // first character read from socket char *cLength; // length of ne

    • PAGE 68

      API in C using winsock 65 recv(fdSock, &cLength[1], 1, 0); recv(fdSock, &cLength[2], 1, 0); recv(fdSock, &cLength[3], 1, 0); } iLen = (int *)cLength; } // read 3 bytes else if ((cFirstChar & 0xC0) == 0xC0) { DEBUG ? printf("3-byte encoded length\n") : 0; if (iLittleEndian) { cLength[2] = cFirstChar; cLength[2] &= 0x3f; // mask out the 1st 2 bits recv(fdSock, &cLength[1], 1, 0); recv(fdSock, &cLength[0], 1, 0); } else { cLength[1] = cFirstChar; cLength[1] &= 0x3f; // mask out the 1st 2 bits recv(fdS

    • PAGE 69

      API in C using winsock } iLen = (int *)cLength; } // assume 1-byte encoded length...

    • PAGE 70

      API in C using winsock // read iBytesToRead from the socket iBytesRead = recv(fdSock, szTmpWord, iBytesToRead, 0); // terminate szTmpWord szTmpWord[iBytesRead] = 0; // concatenate szTmpWord to szRetWord strcat(szRetWord, szTmpWord); // subtract the number of bytes we just read from iLen iLen -= iBytesRead; } // deallocate szTmpWord free(szTmpWord); DEBUG ? printf("word = %s\n", szRetWord) : 0; return szRetWord; } else { return NULL; } } /***************************************************************

    • PAGE 71

      API in C using winsock // check to see if we can get a return value from the API if (strstr(szWord, "!done") != NULL) { DEBUG ? printf("return sentence contains !done\n") : 0; stReturnSentence.iReturnValue = DONE; } else if (strstr(szWord, "!trap") != NULL) { DEBUG ? printf("return sentence contains !trap\n") : 0; stReturnSentence.iReturnValue = TRAP; } else if (strstr(szWord, "!fatal") != NULL) { DEBUG ? printf("return sentence contains !fatal\n") : 0; stReturnSentence.

    • PAGE 72

      API in C using winsock DEBUG ? printf("readBlock\n") : 0; do { stSentence = readSentence(fdSock); DEBUG ? printf("readSentence succeeded.\n") : 0; addSentenceToBlock(&stBlock, &stSentence); DEBUG ? printf("addSentenceToBlock succeeded\n") : 0; } while (stSentence.iReturnValue == 0); DEBUG ? printf("readBlock completed successfully\n") : 0; return stBlock; } /******************************************************************** * Initialize a new block * Set iLength to 0.

    • PAGE 73

      API in C using winsock /******************************************************************** * Print a block. * Output a Block with printf.

    • PAGE 74

      API in C using winsock // update iLength stBlock->iLength = iNewLength; DEBUG ? printf("addSentenceToBlock stBlock->iLength=%d\n", stBlock->iLength) : 0; } /******************************************************************** * Initialize a new sentence ********************************************************************/ void initializeSentence(struct Sentence *stSentence) { DEBUG ? printf("initializeSentence\n") : 0; stSentence->iLength = 0; stSentence->iReturnValue = 0; } /***************************

    • PAGE 75

      API in C using winsock else { stSentence->szSentence = realloc(stSentence->szSentence, iNewLength * sizeof stSentence->szSentence + 1); } // allocate mem for the full word string stSentence->szSentence[stSentence->iLength] = malloc(strlen(szWordToAdd) + 1); // copy word string to the sentence strcpy(stSentence->szSentence[stSentence->iLength], szWordToAdd); // update iLength stSentence->iLength = iNewLength; } /******************************************************************** * Add a partial word to

    • PAGE 76

      API in C using winsock printf("Sentence iReturnValue = %d\n", stSentence->iReturnValue); for (i=0; iiLength; i++) { printf(">>> %s\n", stSentence->szSentence[i]); } printf("\n"); } /******************************************************************** * MD5 helper function to convert an md5 hex char representation to * binary representation.

    • PAGE 77

      API in C using winsock /******************************************************************** * MD5 helper function to calculate and return hex representation * of an MD5 digest stored in binary.

    • PAGE 78

      API in C using winsock else if (cToConvert[0] == 'c' || cToConvert[0] == 'C') { iAccumulated += 16*12; } else if (cToConvert[0] == 'b' || cToConvert[0] == 'B') { iAccumulated += 16*11; } else if (cToConvert[0] == 'a' || cToConvert[0] == 'A') { iAccumulated += 16*10; } else { iAccumulated += 16 * atoi(cString0); } // now look @ the second car in the 16^0 place if (cToConvert[1] == 'f' || cToConvert[1] == 'F') { iAccumulated += 15; } else if (cToConvert[1] == 'e' || cToConvert[1] == 'E') { iAccumulated += 14

    • PAGE 79

      API in C using winsock 76 DEBUG ? printf("%d\n", iAccumulated) : 0; return (char)iAccumulated; } /******************************************************************** * Test whether or not this system is little endian at RUNTIME * Courtesy: http://download.osgeo.org/grass/grass6_progman/endian_8c_source.html ********************************************************************/ int isLittleEndian(void) { union { int testWord; char testByte[sizeof(int)]; } endianTest; endianTest.

    • PAGE 80

      Manual:API Python3 77 Manual:API Python3 Summary Since python language have introduced changes to syntax when going from 2.x to 3.x some adjustments had to be made for old code from API. Code for Python3 code #!/usr/bin/python3 import sys, posix, time, binascii, socket, select import hashlib class ApiRos: "Routeros api" def __init__(self, sk): self.sk = sk self.currenttag = 0 def login(self, username, pwd): for repl, attrs in self.talk(["/login"]): chal = binascii.unhexlify((attrs['=ret']).

    • PAGE 81

      Manual:API Python3 def writeSentence(self, words): ret = 0 for w in words: self.writeWord(w) ret += 1 self.writeWord('') return ret def readSentence(self): r = [] while 1: w = self.readWord() if w == '': return r r.append(w) def writeWord(self, w): print(("<<< " + w)) self.writeLen(len(w)) self.writeStr(w) def readWord(self): ret = self.readStr(self.readLen()) print((">>> " + ret)) return ret def writeLen(self, l): if l < 0x80: self.writeStr(chr(l)) elif l < 0x4000: l |= 0x8000 self.

    • PAGE 82

      Manual:API Python3 self.writeStr(chr((l >> 16) & 0xFF)) self.writeStr(chr((l >> 8) & 0xFF)) self.writeStr(chr(l & 0xFF)) def readLen(self): c = ord(self.readStr(1)) if (c & 0x80) == 0x00: pass elif (c & 0xC0) == 0x80: c &= ~0xC0 c <<= 8 c += ord(self.readStr(1)) elif (c & 0xE0) == 0xC0: c &= ~0xE0 c <<= 8 c += ord(self.readStr(1)) c <<= 8 c += ord(self.readStr(1)) elif (c & 0xF0) == 0xE0: c &= ~0xF0 c <<= 8 c += ord(self.readStr(1)) c <<= 8 c += ord(self.readStr(1)) c <<= 8 c += ord(self.

    • PAGE 83

      Manual:API Python3 80 if s == '': raise RuntimeError("connection closed by remote end") ret += s.decode('UTF-8', 'replace') return ret def main(): s = None for res in socket.getaddrinfo(sys.argv[1], "8728", socket.AF_UNSPEC, socket.SOCK_STREAM): af, socktype, proto, canonname, sa = res try: s = socket.socket(af, socktype, proto) except (socket.error, msg): s = None continue try: s.connect(sa) except (socket.error, msg): s.close() s = None continue break if s is None: print ('could not open socket') sys.

    • PAGE 84

      Manual:API Python3 81 if __name__ == '__main__': main() file |api client in python3 [1] References [1] http:/ / wiki. mikrotik. com/ images/ 6/ 6b/ Api. txt API multiplatform from townet MKAPI: LIBRARY TO CONTROL MIKROTIK BOARDS This article refers to the library developed by townet to control a mikrotik boad using the api, and to the example applications developed using the same library. The library is written in C++, and works under Linux and under Windows. You can download the lybrary and www.

    • PAGE 85

      API multiplatform from townet }; USE OF MKAPI LIBRARY To write a C program using MkApi you need to include the library when you compile your application, and yu need to add the source file “MkApi.h” to ypur C sources. You need to istantiate an object of the class “mikrotikboard”: mikrotikboard mk; Then you need to ask for a login: AnsiString res = mk.login("192.168.1.

    • PAGE 86

      API multiplatform from townet { AnsiString line = parse_line(&c); //Here the variable line contains only a line from the reply. char adr[30]; char interf[20]; parse_par(line, "address=", adr); parse_par(line, "interface=", interf); printf("Interface %s \taddress %s\n", interf, adr); }; }; More easily the function parse_par can be used to read the first occourrence of a parameter and to cut the original string, so you can gradually read all the results, in this way: mikrotikboard mk; AnsiString res=mk.

    • PAGE 87

      API multiplatform from townet MKDeal - tool di interfaccia mikrotik uso: MKDeal ip utente password -c comando (per comandi one-shot) MKDeal ip utente password -i comando (per gestire comandi interattivi) MKDeal -f nomefile -c comando (per gestire comandi su piu' board) Il file deve avere come formato IP#USER#PASS su ogni riga risposte: NUM Err oppure NUM Ok seguito dai dati -1 : parametri errati -2 connessione fallita -3 trap generica -4 password errata -5 trap su comando 0 corretto Some example: ./mik 192.

    • PAGE 88

      API multiplatform from townet back from a command, are ever integer numbers, but this ones doesn’t start from the number zero, like working in telnet. To set the address of an ethernet port, for example, you may need to execute a print command to read all the id’s, and then to execute the “set” command passing the id of the line you really want to change.

    • PAGE 89

      MikroNode 86 MikroNode Mikronode Full-Featured asynchronous Mikrotik API interface for NodeJS [1]. var api = require('mikronode'); var connection = new api('192.168.0.1','admin','password'); connection.connect(function(conn) { var chan=conn.openChannel(); chan.write('/ip/address/print',function() { chan.on('done',function(data) { var parsed = api.parseItems(data); parsed.forEach(function(item) { console.log('Interface/IP: '+item.interface+"/"+item.address); }); chan.close(); conn.

    • PAGE 90

      MikroNode 87 TODO • • • • Cleanup login section in connect method. Re-design code to hide internal methods and variables. Utilize promises. Write tests to make sure everything keeps working while making above changes. API Connection Object Calling new api(host,user,pass,options) returns a connection object. The options argument is optional. If specified, it is an object with these fields: • timeout: number of seconds to wait before timing out due to inactivity. • debug: a value between 0 and 5.

    • PAGE 91

      MikroNode 88 unaltered. • channel.close(force) Close the channel. If there are any commands still waiting to be executed, they will be completed before closing the channel. If force is TRUE, then the channel is immediately closed. If the channel is running, the cancel command is sent to stop any running listen commands, or potentially long running output. Examples Connect to a Mikrotik, and add an address to ether1 var api = require('mikronode'); var connection = new api('192.168.0.

    • PAGE 92

      MikroNode 89 chan3.write(['/interface/set','=disabled=yes','=.id=ether1'],function(chan) { chan.on('done',function(d,chan) { // We do this here, 'cause we want channel 4 to write after channel 3 is done. var chan4=conn.openChannel(4); // We'll use this later. chan4.closeOnDone(true); chan4.write(['/interface/set','=disabled=no','=.id=ether1'],function() { var chan5=conn.openChannel(5); chan5.closeOnDone(true); chan5.write('/interface/getall',function(chan) { chan.on('done',function(data) { packets=api.

    • PAGE 93

      MikroNode 90 console.log('Interface: '+JSON.stringify(packet)); }); listenChannel.close(); // This should call the /cancel command to stop the listen. }); }); actionChannel.close(); // The above commands will complete before this is closed. }); License (The MIT License) Copyright (c) 2011 Brandon Myers trakkasure@gmail.

    • PAGE 94

      API in Java 91 API in Java Summary RouterOS API access library written in Java. This code is also capable to connect to IPv6 addresses. Licensing Code is provided as is and can be freely used freely. I, as a writer of code, am not responsible for anything that may arise from use of this code. Usage Simple example how this can be used.

    • PAGE 95

      API in Java 92 } @Override public void run() { String s = ""; while (true) { try { s = aConn.getData(); if (s != null) { t3A.outputHere(s); if (s.contains("!done")) { } } } catch (InterruptedException ex) { Logger.getLogger(DataReceiver.class.getName()).log(Level.SEVERE, null, ex); } } } } Code Code that is ready to be compiled and used. In some places some comments may be missing. Compiled jar file [1] of same java classes ApiConn.

    • PAGE 96

      API in Java 93 private DataOutputStream out = null; private DataInputStream in = null; private String ipAddress; private int ipPort; private boolean connected = false; private String message = "Not connected"; private ReadCommand readCommand = null; private WriteCommand writeCommand = null; private Thread listener = null; LinkedBlockingQueue queue = new LinkedBlockingQueue(40); /** * Constructor of the connection class * @param ipAddress - IP address of the router you want to conenct to * @param ipPort -

    • PAGE 97

      API in Java 94 * to get IP address of the connection. Reads data from socket created. * @return InetAddress */ public InetAddress getIpAddress() { return sock == null ? null : sock.getInetAddress(); } /** * returns ip address that socket is asociated with. * @return InetAddress */ public InetAddress getLocalIpAddress() { return sock == null ? null : sock.getLocalAddress(); } /** * Socket remote port number * @return */ public int getPort() { return sock == null ? null : sock.

    • PAGE 98

      API in Java 95 /** * exeecutes already set command. * @return returns status of the command sent */ public String runCommand() { return writeCommand.runCommand(); } /** * Tries to fech data that is repllied to commands sent. It will wait till it can return something. * @return returns data sent by RouterOS * @throws java.lang.InterruptedException */ public String getData() throws InterruptedException { String s = (String) queue.take(); return s; } /** * returns command that is set at this moment.

    • PAGE 99

      API in Java 96 String transition = tmp[tmp.length - 1]; String chal = ""; chal = Hasher.hexStrToStr("00") + new String(password) + Hasher.hexStrToStr(transition); chal = Hasher.hashMD5(chal); String m = "/login\n=name=" + name + "\n=response=00" + chal; s = this.sendCommand(m); try { s = this.getData(); } catch (InterruptedException ex) { Logger.getLogger(ApiConn.class.getName()).log(Level.SEVERE, null, ex); return "failed read #2"; } if (s.contains("!done")) { if (!s.

    • PAGE 100

      API in Java } Hasher.java Helper functions to perform some tasks package libAPI; /* * Helper.java * * Created on 08 June 2007, 11:25 * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ import java.security.MessageDigest; import java.security.

    • PAGE 101

      API in Java 98 String hex = Integer.toHexString(0xFF & messageDigest[i]); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } return hexString.toString(); } /** * converts hex value string to normal strint for use with RouterOS API * @param s - hex string to convert to * @return - converted string. */ static public String hexStrToStr(String s) { String ret = ""; for (int i = 0; i < s.length(); i += 2) { ret += (char) Integer.parseInt(s.

    • PAGE 102

      API in Java 99 private DataInputStream in = null; LinkedBlockingQueue queue = null; /** * Creates a new instance of CommandRead * @param in - Data input stream of socket * @param queue - data output inteface */ public ReadCommand(DataInputStream in, LinkedBlockingQueue queue) { this.in = in; this.queue = queue; } @Override public void run() { byte b = 0; String s = ""; char ch; int a = 0; while (true) { int sk = 0; try { a = in.

    • PAGE 103

      API in Java 100 return; } sk = a ^ 0xC00000; } else { if (a < 0xF0) { try { for (int i = 0; i < 3; i++) { a = a << 8; a += in.read(); } } catch (IOException ex) { Logger.getLogger(ReadCommand.class.getName()).log(Level.SEVERE, null, ex); return; } sk = a ^ 0xE0000000; } else { if (a < 0xF8) { try { a = 0; for (int i = 0; i < 5; i++) { a = a << 8; a += in.read(); } } catch (IOException ex) { Logger.getLogger(ReadCommand.class.getName()).log(Level.

    • PAGE 104

      API in Java 101 } else { try { queue.put(s); } catch (InterruptedException ex) { ex.printStackTrace(); System.out.println("exiting reader"); return; } s = ""; } } } } WriteCommand.java All writing to RouterOS API is done using this. package libAPI; /* * To change this template, choose Tools | Templates * and open the template in the editor. */ import java.io.DataOutputStream; import java.io.IOException; import java.util.logging.Level; import java.util.logging.

    • PAGE 105

      API in Java 102 WriteCommand setCommand(String command) { this.command = command.trim(); return this; } String getCommand() { return command; } private byte[] writeLen(String command) { Integer i = null; String s = ""; String ret = ""; if (command.length() < 0x80) { i = command.length(); } else if (command.length() < 0x4000) { i = Integer.reverseBytes(command.length() | 0x8000); } else if (command.length() < 0x20000) { i = Integer.reverseBytes(command.length() | 0xC00000); } else if (command.

    • PAGE 106

      API in Java 103 for (byte c : command.getBytes("US-ASCII")) { ret[i++] = c; } } else { String[] str = command.split("\n"); int i = 1; int[] iTmp = new int[str.length]; for (int a = 0; a < str.length; a++) { iTmp[a] = writeLen(str[a]).length + str[a].length(); } for (int b : iTmp) { i += b; } ret = new byte[i]; int counter = 0; for (int a = 0; a < str.length; a++) { int j = 0; byte[] b = writeLen(str[a]); for (j = 0; j < b.length; j++) { ret[counter++] = b[j]; } for (byte c : str[a].

    • PAGE 107

      API In CPP 104 API In CPP This is written in C++. The code is based highly on the code from API In C. I like the way this was done in respect to how easy it is to send a command and get a block of sentences back that are easily parsed. I have removed all the memory leaks and converted it entirely to C++. There is only a few places using any memory allocation and that is mostly in the encoding as its much easier to do with dynamic char arrays.

    • PAGE 108

      API In CPP any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically.

    • PAGE 109

      API In CPP 106 /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ Pre-requisite MD5 calculation function source file (md5.c) /* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.

    • PAGE 110

      API In CPP 107 copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program.

    • PAGE 111

      API In CPP #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define 108 T17 T18 T19 T20 T21 T22 T23 T24 T25 T26 T27 T28 T29 T30 T31 T32 T33 T34 T35 T36 T37 T38 T39 T40 T41 T42 T43 T44 T45 T46

    • PAGE 112

      API In CPP 109 #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs.

    • PAGE 113

      API In CPP 110 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1.

    • PAGE 114

      API In CPP SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations.

    • PAGE 115

      API In CPP SET(c, SET(b, SET(a, SET(d, SET(c, SET(b, #undef SET 112 d, c, b, a, d, c, a, d, c, b, a, d, b, 3, 16, T43); a, 6, 23, T44); d, 9, 4, T45); c, 12, 11, T46); b, 15, 16, T47); a, 2, 23, T48); /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations.

    • PAGE 116

      API In CPP 113 pms->abcd[0] pms->abcd[1] pms->abcd[2] pms->abcd[3] = = = = 0x67452301; /*0xefcdab89*/ T_MASK ^ 0x10325476; /*0x98badcfe*/ T_MASK ^ 0x67452301; 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length.

    • PAGE 117

      API In CPP static const md5_byte_t pad[64] 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; md5_byte_t data[8]; int i; 114 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64.

    • PAGE 118

      API In CPP 115 #define DONE 1 #define TRAP 2 #define FATAL 3 class Sentence { std::vector strWords; int returnType; // vecor of strings representing individual words // return type of sentence void Tokenize(const std::string &str, std::vector &tokens, const std::string &delimiters = " "); public: void SetReturnType(int returnTypeIn) { returnType = returnTypeIn; } int GetReturnType() { return returnType; } void AddWord(const std::string &strWordToAdd) { strWords.

    • PAGE 119

      API In CPP 116 /******************************************************************** * Print a sentence. ********************************************************************/ bool Sentence::Print() { DEBUG ? printf("Sentence Word Count = %d\n", strWords.size()) : 0; DEBUG ? printf("Sentence returnType = %d\n", returnType) : 0; for (int i = 0; i < strWords.size(); ++i) { printf("%s\n", strWords[i].

    • PAGE 120

      API In CPP 117 } /******************************************************************** * Add a Sentence to a block ********************************************************************/ void Block::AddSentence(const Sentence &sentence) { sentences.push_back(sentence); DEBUG ? printf("AddSentenceToBlock Size=%d\n", sentences.size()) : 0; } /******************************************************************** * Print a block.

    • PAGE 121

      API In CPP 118 mt.WriteSentence(sentence); // receive and print block from the API mt.ReadBlock(block); block.Print(); } catch (int e) { if(e == NOCONNECT) printf("Could not connect.\n"); if(e == NOLOGIN) printf("Could not login.\n"); } Original Author: Joey Gentry (www.Murderdev.com) Feel freel to ask me questions but I can't guarantee I will be able to answer them all. No warranties are provided with this code. This was written/converted for my iPhone app to allow accessing a Mikrotik router.

    • PAGE 122

      API In CPP 119 #include "MikrotikAPITypes.

    • PAGE 123

      API In CPP 120 Mikrotik API source file (MikrotikAPI.cpp) #include "MikrotikAPI.h" using namespace std; MikrotikAPI::MikrotikAPI(const string &strIpAddress, const string &strUsername, const string &strPassword, int port) { Connect(strIpAddress, port); if(fdSock != -1) { // attempt login int loginResult = Login(strUsername, strPassword); if (!loginResult) { throw NOLOGIN; Disconnect(); printf("Invalid username or password.\n"); } else { printf("Logged in successfully.

    • PAGE 124

      API In CPP 121 address.sin_port = htons(port); addressSize = sizeof(address); DEBUG ? printf("Connecting to %s\n", strIpAddress.c_str()) : 0; connectResult = connect(fdSock, (struct sockaddr *)&address, addressSize); if(connectResult==-1) { perror ("Connection problem"); Disconnect(); fdSock = -1; } else { DEBUG ? printf("Successfully connected to %s\n", strIpAddress.c_str()) : 0; } // determine endianness of this machine // iLittleEndian will be set to 1 if we are // on a little endian machine...

    • PAGE 125

      API In CPP 122 char cNull[1] = {0}; //Send login message WriteWord("/login"); WriteWord(cNull); ReadSentence(readSentence); DEBUG ? readSentence.Print() : 0; if (readSentence.GetReturnType() != DONE) { printf("Error.\n"); } else { // extract md5 string from the challenge sentence char *strWord = new char [readSentence[1].size() + 1]; strcpy(strWord, readSentence[1].

    • PAGE 126

      API In CPP 123 DEBUG ? writeSentence.Print() : 0; WriteSentence(writeSentence); ReadSentence(readSentence); DEBUG ? readSentence.Print() : 0; if (readSentence.

    • PAGE 127

      API In CPP 124 if (littleEndian) { encodedLengthData[0] = lengthData[2] | 0xc0; encodedLengthData[1] = lengthData[1]; encodedLengthData[2] = lengthData[0]; } else { encodedLengthData[0] = lengthData[1] | 0xc0; encodedLengthData[1] = lengthData[2]; encodedLengthData[2] = lengthData[3]; } write (fdSock, encodedLengthData, 3); } else if (messageLength < 0x10000000) { // write 4 bytes (untested) DEBUG ? printf("messageLength < 0x10000000.

    • PAGE 128

      API In CPP 125 ********************************************************************/ void MikrotikAPI::WriteSentence(Sentence &writeSentence) { if (writeSentence.Length() == 0) { return; } DEBUG ? printf("Writing sentence\n"): 0; DEBUG ? writeSentence.Print() : 0; for (int i = 0; i < writeSentence.

    • PAGE 129

      API In CPP 126 lengthData[3] &= 0x1f; // mask out the 1st 3 bits read(fdSock, &lengthData[2], 1); read(fdSock, &lengthData[1], 1); read(fdSock, &lengthData[0], 1); } else { lengthData[0] = firstChar; lengthData[0] &= 0x1f; // mask out the 1st 3 bits read(fdSock, &lengthData[1], 1); read(fdSock, &lengthData[2], 1); read(fdSock, &lengthData[3], 1); } messageLength = (int *)lengthData; } else if ((firstChar & 0xC0) == 0xC0) { // read 3 bytes DEBUG ? printf("3-byte encoded length\n") : 0; if (littleEndia

    • PAGE 130

      API In CPP 127 } int retMessageLength = *messageLength; delete messageLength; delete [] lengthData; return retMessageLength; } /******************************************************************** * Read a word from the socket * The word that was read is returned as a string ********************************************************************/ void MikrotikAPI::ReadWord(string &strWordOut) { int messageLength = ReadLength(); int bytesToRead = 0; int bytesRead = 0; char *tmpWord; DEBUG ? printf("ReadWo

    • PAGE 131

      API In CPP 128 // deallocate szTmpWord delete [] tmpWord; DEBUG ? printf("Word = %s\n", strWordOut.c_str()) : 0; } } /******************************************************************** * Read a Sentence from the socket * A Sentence struct is returned ********************************************************************/ void MikrotikAPI::ReadSentence(Sentence &sentenceOut) { DEBUG ? printf("ReadSentence\n") : 0; sentenceOut.Clear(); string strWord; ReadWord(strWord); while (!strWord.

    • PAGE 132

      API In CPP 129 } /******************************************************************** * Read Sentence Block from the socket...keeps reading sentences * until it encounters !done, !trap or !fatal from the socket ********************************************************************/ void MikrotikAPI::ReadBlock(Block &block) { Sentence sentence; block.Clear(); DEBUG ? printf("ReadBlock\n") : 0; do { ReadSentence(sentence); DEBUG ? printf("ReadSentence succeeded.\n") : 0; block.

    • PAGE 133

      API In CPP 130 } return strReturn; } /******************************************************************** * MD5 helper function to calculate and return hex representation * of an MD5 digest stored in binary.

    • PAGE 134

      API In CPP 131 // now look @ the second car in the 16^0 place if (hexToConvert[1] == 'f' || hexToConvert[1] == 'F') { accumulated += 15; } else if (hexToConvert[1] == 'e' || hexToConvert[1] == 'E') { accumulated += 14; } else if (hexToConvert[1] == 'd' || hexToConvert[1] == 'D') { accumulated += 13; } else if (hexToConvert[1] == 'c' || hexToConvert[1] == 'C') { accumulated += 12; } else if (hexToConvert[1] == 'b' || hexToConvert[1] == 'B') { accumulated += 11; } else if (hexToConvert[1] == 'a' || hexToConv

    • PAGE 135

      Api php template 132 Api php template This is a php template for working with RouterOS v3 API. Requirements 1. It uses the php api Class found here : link API_PHP_class [1]. 2. It is presented first in the forum here link Forum_link [2] 3. overLIB popup JavaScript library is required for this and its included in the files (zip file) or can be found here:[3] Connected Clients (from the registration table)

    • PAGE 136

      Api php template 133 if ($API->connect('192.168.1.2', 'api', 'api1234')) { this as necessery // Change $ARRAY = $API->comm("/system/resource/print"); $first = $ARRAY['0']; $memperc = ($first['free-memory']/$first['total-memory']); $hddperc = ($first['free-hdd-space']/$first['total-hdd-space']); $mem = ($memperc*100); $hdd = ($hddperc*100); echo "Mikrotik RouterOs 4.

    • PAGE 137

      Api php template 134 echo "
      "; $API->disconnect(); } ?> Registration Tables debug = false; if ($API->connect('192.168.1.

    • PAGE 138

      Api php template noisestrength at ratestx ccqpthroughputack timeoutlast ip802.1x port en.authentication typeencryptiongroup encryptionwmm"; echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['.id'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" .

    • PAGE 139

      Api php template 136 $regtable = $ARRAY[$i]; if ($regtable['ap']=="true") { echo "" . $regtable['ap'] . "
      "; }else{ echo "". $regtable['ap'] ."
      "; } } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; if ($regtable['wds']=="true") { echo "" . $regtable['wds'] . "
      "; }else{ echo "". $regtable['wds'] .

    • PAGE 140

      Api php template 137 "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo popup('Data', 'Packets ' . $regtable['packets'] . '
      Bytes ' . $regtable['bytes'] . '
      Frames ' . $regtable['frames'] . '
      Frame-Bytes ' . $regtable['frame-bytes'] . '
      hw-frames ' . $regtable['hw-frames'] . '
      hw-frame-bytes ' . $regtable['hw-frame-bytes'] . '
      tx-frames-timed-out ' .

    • PAGE 141

      Api php template $regtable = $ARRAY[$i]; echo "" . $regtable['signal-strength'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['signal-to-noise'] .

    • PAGE 142

      Api php template "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['p-throughput'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['ack-timeout'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['last-ip'] .

    • PAGE 143

      Api php template echo "". $regtable['802.1x-port-enabled'] ."
      "; } } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['authentication-type'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['encryption'] .

    • PAGE 144

      Api php template $regtable['wmm-enabled'] . "
      "; }else{ echo "". $regtable['wmm-enabled'] ."
      "; } } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['comment'] .

    • PAGE 145

      Api php template debug = false; if ($API->connect('192.168.1.

    • PAGE 146

      Api php template 143 for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['type'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; if ($regtable['dynamic']=="true") { echo "" . $regtable['dynamic'] . "
      "; }else{ echo "". $regtable['dynamic'] .

    • PAGE 147

      Api php template $regtable = $ARRAY[$i]; echo "" . $regtable['mtu'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['l2mtu'] .

    • PAGE 148

      Api php template debug = false; if ($API->connect('192.168.1.

    • PAGE 149

      Api php template 146 echo "" . $regtable['name'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['mtu'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['mac-address'] .

    • PAGE 150

      Api php template { $regtable = $ARRAY[$i]; echo "" . $regtable['interface-type'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['mode'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['ssid'] .

    • PAGE 151

      Api php template 148 echo "" . $regtable['band'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['scan-list'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['antenna-mode'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo popup('WDS', 'WDS Mode ' .

    • PAGE 152

      Api php template 149 echo "" . $regtable['default-authentication'] . "
      "; }else{ echo "". $regtable['default-authentication'] ."
      "; } } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; if ($regtable['default-forwarding']=="true") { echo "" . $regtable['default-forwarding'] . "
      "; }else{ echo "". $regtable['default-forwarding'] .

    • PAGE 153

      Api php template 150 echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; if ($regtable['hide-ssid']=="true") { echo "" . $regtable['hide-ssid'] . "
      "; }else{ echo "". $regtable['hide-ssid'] ."
      "; } } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['security-profile'] .

    • PAGE 154

      Api php template 151 { $regtable = $ARRAY[$i]; if ($regtable['running']=="true") { echo "" . $regtable['running'] . "
      "; }else{ echo "". $regtable['running'] ."
      "; } } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; if ($regtable['disabled']=="true") { echo "" . $regtable['disabled'] . "
      "; }else{ echo "". $regtable['disabled'] .

    • PAGE 155

      Api php template 152 Hotspot Hosts List debug = false; if ($API->connect('192.168.1.

    • PAGE 156

      Api php template 153 } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['mac-address'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['address'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['to-address'] .

    • PAGE 157

      Api php template echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['uptime'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['keepalive-timeout'] . "
      "; } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; echo "" . $regtable['found-by'] .

    • PAGE 158

      Api php template 155 } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; if ($regtable['authorized']=="true") { echo "" . $regtable['authorized'] . "
      "; }else{ echo "". $regtable['authorized'] ."
      "; } } echo ""; for ($i=0; $i<250; $i++) { $regtable = $ARRAY[$i]; if ($regtable['bypassed']=="true") { echo "" . $regtable['bypassed'] .

    • PAGE 159

      Api php template echo ""; echo "
      Debug:"; echo "
      "; print_r($ARRAY); $API->disconnect(); } ?> References [1] http:/ / wiki. mikrotik. com/ wiki/ API_PHP_class [2] http:/ / forum. mikrotik. com/ viewtopic. php?f=9& t=50176 [3] http:/ / www. bosrup. com/ web/ overlib/ ?Download API in VB dot NET This is VB.NET class for connecting and working with Mikrotik API. It will give you basic connectivity so you can log in and send and receive commands.

    • PAGE 160

      API in VB dot NET 157 Send("/login") Send("=name=" + user) Send("=response=00" + EncodePassword(pass, hash), True) Dim res = Read() If (res(0) = "!done") Then Return True Else Return False End Function Function EncodePassword(ByVal pass As String, ByVal challange As String) As String Dim hash_byte(challange.Length / 2 - 1) As Byte For i = 0 To challange.Length - 2 Step 2 hash_byte(i / 2) = Byte.Parse(challange.Substring(i, 2), Globalization.NumberStyles.HexNumber) Next Dim response(pass.

    • PAGE 161

      API in VB dot NET 158 Case 0 output.Add(o) If o.Substring(0, 5) = "!done" Then Exit While Else o = "" Continue While End If Case Is < &H80 count = tmp(3) Case Is < &HC0 count = BitConverter.ToInt32(New Byte() {tcpStream.ReadByte(), tmp(3), 0, 0}, 0) ^ &H8000 Case Is < &HE0 tmp(2) = tcpStream.ReadByte() count = BitConverter.ToInt32(New Byte() {tcpStream.ReadByte(), tmp(2), tmp(3), 0}, 0) ^ &HC00000 Case Is < &HF0 tmp(2) = tcpStream.ReadByte() tmp(1) = tcpStream.ReadByte() count = BitConverter.

    • PAGE 162

      API in VB dot NET 159 Dim tmp = BitConverter.GetBytes(l Or &HE0000000) Return New Byte() {tmp(3), tmp(2), tmp(1), tmp(0)} Else Dim tmp = BitConverter.GetBytes(l) Return New Byte() {&HF0, tmp(3), tmp(2), tmp(1), tmp(0)} End If End Function End Class Example Module Module1 Sub Main() Dim mk = New Mikrotik("mikrotik") If Not mk.Login("admin", "PpAaSsWwOoRrDd") Then Console.WriteLine("Cant log in") mk.Close() Console.ReadLine() Return End If mk.Send("/system/clock/getall", True) For Each row In mk.

    • PAGE 163

      RouterOS PHP class RouterOS PHP class • • • • Author: Kamil Trzcinski E-mail: ayufan(at)osk-net(dot)pl WWW: [1] License: GPL • added callbacks • added btest • initial release The main purpose of another RouterOS PHP API class it to simplify configuration update processes. Example: We have about 20 access points and for each of them we have connected about 20 wds links. Using automatic configuration process we can store information about all wds links in one place. It can be MySQL database.

    • PAGE 164

      RouterOS PHP class 161 • static function connect($host, $login, $password, $port = 8728, $timeout = 5) Connects to new RouterOS using specified "host" with specified "login" and "password" on "port". $conn = RouterOS::connect("192.168.10.11", "admin", "adminpassword"); • public function setTimeout($timeout = 5) Set socket timeout in seconds. $conn->setTimeout(10); • function dispatch(&$continue) Dispatches comming messages from server to functions executed as callbacks.

    • PAGE 165

      RouterOS PHP class 162 [tx-ccq] => 95 [p-throughput] => 5361 [ack-timeout] => 30 [last-ip] => 192.168.9.14 [802.1x-port-enabled] => true [wmm-enabled] => false ) [1] => Array ( [.

    • PAGE 166

      RouterOS PHP class 163 Array ( [00:1F:1F:XX:XX:XX] => Array ( [.id] => *2 [interface] => ap11 [mac-address] => 00:1F:1F:XX:XX:XX ) [00:0C:42:XX:XX:XX] => Array ( [.id] => *7 [interface] => backbone [mac-address] => 00:0C:42:XX:XX:XX ) ) • function set($cmd, $args, $callback = FALSE) Set item or command value. $conn->set("/ip/firewall/filter", array(".id"=>"*10", "chain"=>"forward", "action"=>"reject"); • function reboot() Reboots RouterOS. Returns TRUE on success.

    • PAGE 167

      RouterOS PHP class $conn->unsett("/queue/simple", "*10", "time"); • function btest($address, $speed = "1M", $protocol = "tcp", $callback = FALSE) Perform a bandwidth-test. Supports only transmit and it should be used as asynchronous command, ie. callback. • function scan($id, $duration="00:02:00", $callback = FALSE) Perform a remote wireless scan. Before scanning set stream interval to larger value than duration. Returns array of results on success. $interfaces = $conn->getall("/interface/wireless", ".

    • PAGE 168

      RouterOS PHP class 165 [snr] => 45 [radio-name] => 3713 ) ) :@ @@...

    • PAGE 169

      RouterOS PHP class $name = $name[0]; if($dests[$name]) die("destination $dest already defined!\n"); $tag = $conn->btest($name, $speed, $protocol, btestCallback); if($tag === FALSE) continue; $tags[$tag] = $name; $dests[$name] = array("dest" => $dest, "speed" => $speed, "protocol" => $protocol); } // print header ncurses_init(); ncurses_nl(); printStatus(); // dispatch messages $continue = TRUE; $conn->dispatch($continue); exit; function btestCallback($conn, $state, $results) { global $dests, $tags, $

    • PAGE 170

      RouterOS PHP class } // not running if($results["status"] != "running") { // state changed if($status[$dest] != $results["status"]) { $status[$dest] = $results["status"]; printStatus(); } // restart btest (in error state) if($results["status"] != "connecting") { $conn->cancel($results[".

    • PAGE 171

      RouterOS PHP class if($data < $multi*$multi) { return round($data/$multi, 1) . "k$postfix"; } if($data < $multi*$multi*$multi) { return round($data/$multi/$multi, 1) . "M$postfix"; } return round($dat /$multi/$multi/$multi, 1) .

    • PAGE 172

      RouterOS PHP class $lines = array(); foreach($dests as $dest=>$desc) { $lines[] = array("host"=>$desc["dest"], "speed"=>$desc["speed"], "proto"=>$desc["protocol"], "status"=>$status[$dest], "current"=>$current[$dest], "average"=>$average[$dest], "%"=>$percent[$dest]); } ncurses_addstr(printTable($header, $lines)); ncurses_refresh(); } ?> Parser class to load configuration from file and perform differencing configuration update.

    • PAGE 173

      RouterOS PHP class left-value, right-value - either string or variable if %version% ~= 4.* # execute commands for version 4.* else # execute commands for all other versions 4.

    • PAGE 174

      RouterOS PHP class Add new section alias of type to configuration update with comma delimeted group keys and list of default_key. See RouterOSParser::section function. section firewall-filter /ip/firewall/filter addset_order section wireless-wds /interface/wireless/wds addset name disabled=no disable [alias] [alias2]... Remove section from configuration update. disable firewall-filter queue-tree [cmd-name] [arg1] [arg2]... Execute user defined function with args. my_first_function 192.168.10.1 192.168.10.

    • PAGE 175

      RouterOS PHP class 172 Add or set config for specified short command. $parser->config("firewall-filter", "action=drop chain=forward"); $parser->config("firewall-filter", array("action"=>"drop", "chain"=>"forward")); $parser->config("connection-tracking", "enabled=no"); • function ignore($cmd, $line) Ignore specified item from synchronization. Has precedence before "pass". Muliple ignore or pass rules can be added.

    • PAGE 176

      RouterOS PHP class $parser->parseFile(array("config line 1", "config line 2", "config line 3")); • function call($cmd, $args) Execute defined function with specified args as array in current parser context. Returns function return value. $parser->call("test_function", array("192.168.10.1", "1.2.3.4")); • function updateSection($conn, $alias) Perform specified section $alias update for specified RouterOS $conn connection. All update logs are in $parser->logs.

    • PAGE 177

      RouterOS PHP class $parser->update($conn); // perform update ?> # load predefined global OSPF configuration require ospf.cfg # set device name, clock and ntp-client section identity /system/identity value section clock /system/clock value section ntp-client /system/ntp/client value set identity name=%name% set clock time-zone-name=Europe/Warsaw set ntp-client enabled=true mode=unicast primary-ntp=192.168.10.5 secondary-ntp=192.168.10.

    • PAGE 178

      RouterOS PHP class redistribute-rip=no redistribute-bgp=no metric-default=2 metric-connected=2 metric-static=1 metric-rip=1 metric-bgp=1 endif # add sections for ospf configuration section ospf-area /routing/ospf/area addset name,area-id section ospf-interface /routing/ospf/interface addset interface section ospf-network /routing/ospf/network addset network section ospf-area-range /routing/ospf/area/range addset area,range # add configuration add ospf-area name=backbone area-id=0.0.0.

    • PAGE 179

      API command notes 176 API command notes Summary This page contains some information about details of API commands, examples or use-cases. For more detailed information refer to API. Note: Till version 4.6 including API logins where shown as winbox logins. Since 4.7 this behaviour is changed and API logins will be correctly recognised and displayed as API logins.

    • PAGE 180

      API command notes 177 result of double Tab [admin@MikroTik] > ip address add broadcast comment copy-from disabled netmask network address interface Note: Not all attributes will have full or precise description in CLI, but all attributes will have precise and full description of values accepted by attribute Building API sentence: /ip/address/add =address=192.168.88.1/24 =interface=ether1 Result of execution of this command will be IP address added on interface ether1 same as in CLI.

    • PAGE 181

      API command notes 178 All this boils down to this, where XX is encoded word legth, aaaa is word and 0x00 is terminating zero single line sentence XXaaaa0x00 multiple line sentence XXaaaa XXaaaa0x00 or XXaaaa XXaaaa XXaaaa0x00 Note: Usually API implementations takes care of encoding word length part and user only have to worry to make sure that correct method/function is used to send words over to router and make sure words make meaningful sentence for execution Scripting and API It is possible to access

    • PAGE 182

      API command notes 179 !fatal !fatal can be received only in cases when API is closing connection: • too many commands are sent to router prior login • there is error in authentication that is not recoerable • /quit command is sent to router.

    • PAGE 183

      API command notes will result in: !trap =category=4 =message=already have device with such name !done While adding several entries with same name as static DNS entries is completely legal, addressing entries using name value for .id is NOT. Entry with name=example.com address=192.168.88.1 added before. /ip/dns/static/set =.id=example.com =address=3.3.3.

    • PAGE 184

      API command notes =tx-bits-per-second=11266 !re =rx-packets-per-second=8 =rx-drops-per-second=0 =rx-errors-per-second=0 =rx-bits-per-second=14179 =tx-packets-per-second=4 =tx-drops-per-second=0 =tx-errors-per-second=0 =tx-bits-per-second=8591 !re =rx-packets-per-second=4 =rx-drops-per-second=0 =rx-errors-per-second=0 =rx-bits-per-second=2312 =tx-packets-per-second=2 =tx-drops-per-second=0 =tx-errors-per-second=0 =tx-bits-per-second=3039 !re =rx-packets-per-second=5 =rx-drops-per-second=0 =rx-errors-per-seco

    • PAGE 185

      API command notes Example /ping =address=192.168.88.1 =count=3 In this case ping returned after duration*count seconds, where duration was default 1 second. !done =ret=3 Ping v5.x and newer it is equivalent of ping available in CLI, but it will give report on averages every time it has result for sent ping. Details • for ease of use it us suggested that it is used with count argument set to some value • Ping returns only when it is interrupted or reached count limit. • Timing results are in form HH:MM:SS.

    • PAGE 186

      API command notes !done Cancel tagging You may cancel every previously executed task. Note however how cancel behaves with specified tag and without. Example without tag Command execution. /ping =address=google.com Reply itself. !re =host=77.252.2.103 =size=56 =ttl=60 =time=00:00:00.022 =sent=1 =received=1 =packet-loss=0 =min-rtt=00:00:00.022 =avg-rtt=00:00:00.022 =max-rtt=00:00:00.022 !re =host=77.252.2.103 =size=56 =ttl=60 =time=00:00:00.026 =sent=2 =received=2 =packet-loss=0 =min-rtt=00:00:00.

    • PAGE 187

      API command notes Example with specified tag Command execution. /ping =address=google.com .tag=22 Reply itself. !re =host=5.226.127.144 =size=56 =ttl=61 =time=00:00:00.008 =sent=1 =received=1 =packet-loss=0 =min-rtt=00:00:00.008 =avg-rtt=00:00:00.008 =max-rtt=00:00:00.008 .tag=22 !re =host=5.226.127.144 =size=56 =ttl=61 =time=00:00:00.025 =sent=2 =received=2 =packet-loss=0 =min-rtt=00:00:00.008 =avg-rtt=00:00:00.016 =max-rtt=00:00:00.025 .tag=22 Cancel command. /cancel =tag=22 .

    • PAGE 188

      API command notes .tag=22 Canceling with additional errors Cancel failed /tool fetch via http. 3 is that tag of /tool/fetch, 7 is a /cancel tag itself. /cancel =tag=3 .tag=7 !trap =category=2 =message=interrupted .tag=3 !done .tag=7 !trap =message=failure: 301 Moved Permanently .tag=3 !done .tag=3 Conclusions As you can see in above examples, tagging sentences lets you easilly determine which command have finished. Please note that every time you cancel !trap =category=2 =message=interrupted is generated.

    • PAGE 189

      API Ruby class API Ruby class Ruby GEM The API Ruby class(es) are now packaged together as a Ruby GEM. The latest GEM is available for download from the author's web site. The current version is 4.0.0 available here: mtik-4.0.0.gem [1] Or you can simply do: gem install mtik RDoc Documentation The author's site also hosts Ruby RDoc documents for the classes implementing this API. The link is: http://www.aarongifford.

    • PAGE 190

      API Ruby class <<< END-OF-SENTENCE >>> >>> >>> >>> '!trap' (5) 'message=cannot log in' (21) '.tag=1' (6) END-OF SENTENCE >>> '!done' (5) >>> '.tag=1' (6) >>> END-OF SENTENCE === LOGIN ERROR: Login failed: cannot log in user@bsdhost:~$ That run was deliberately with the wrong password. Here's the login with the correct password: user@bsdhost:~$ tikcli 10.20.30.1 admin correctpassword <<< '/login' (6) <<< END-OF-SENTENCE >>> >>> >>> >>> '!done' (5) 'ret=857e91c460620a02c3ca72ea7cf6c696' (36) '.

    • PAGE 191

      API Ruby class >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> '!re' (3) '.id=*5' (6) 'name=ether1' (11) 'type=ether' (10) 'mtu=1500' (8) 'l2mtu=1500' (10) 'bytes=26908361008/15001379552' (29) 'packets=34880279/26382227' (25) 'drops=0/0' (9) 'errors=5/0' (10) 'dynamic=false' (13) 'running=true' (12) 'disabled=false' (14) 'comment=' (8) '.tag=4' (6) END-OF SENTENCE >>> '!done' (5) >>> '.

    • PAGE 192

      API Ruby class >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> 'mtu=1524' (8) 'l2mtu=1524' (10) 'bytes=26909135851/15002882324' (29) 'packets=34886461/26387909' (25) 'drops=0/0' (9) 'errors=5/0' (10) 'dynamic=false' (13) 'running=true' (12) 'disabled=false' (14) 'comment=' (8) '.tag=2' (6) END-OF SENTENCE >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> '!re' (3) '.

    • PAGE 193

      API Ruby class >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> '!re' (3) '.id=*5' (6) 'name=ether1' (11) 'type=ether' (10) 'mtu=1524' (8) 'l2mtu=1524' (10) 'bytes=26909143624/15002895110' (29) 'packets=34886524/26387963' (25) 'drops=0/0' (9) 'errors=5/0' (10) 'dynamic=false' (13) 'running=true' (12) 'disabled=false' (14) 'comment=' (8) '.tag=2' (6) END-OF SENTENCE >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> '!re' (3) '.

    • PAGE 194

      API Ruby class >>> 'comment=' (8) >>> '.tag=2' (6) >>> END-OF SENTENCE <<< '/cancel' (7) <<< '=tag=2' (6) <<< END-OF-SENTENCE >>> >>> >>> >>> >>> '!trap' (5) 'category=2' (10) 'message=interrupted' (19) '.tag=2' (6) END-OF SENTENCE === TRAP: 'interrupted' >>> '!done' (5) >>> '.tag=3' (6) >>> END-OF SENTENCE >>> '!done' (5) >>> '.

    • PAGE 195

      API Ruby class 192 # output until we "cancel" the command. We only want to receive 10 responses: $reply_limit = 10 # Execute the command: $reply_count = 0 connection.get_reply_each( "/interface/monitor-traffic", "=interface=ether1", "=.proplist=rx-bits-per-second,tx-bits-per-second" ) do |request_object, reply_sentence| if reply_sentence.

    • PAGE 196

      API Ruby class 193 <<< '/interface/monitor-traffic' (26) <<< '=interface=ether1' (17) <<< '=.proplist=rx-bits-per-second,tx-bits-per-second' (48) <<< '.tag=2' (6) <<< END-OF-SENTENCE >>> '!re' (3) >>> 'rx-bits-per-second=5744844' (26) >>> 'tx-bits-per-second=61787' (24) >>> '.tag=2' (6) >>> END-OF SENTENCE {"!re"=>nil, "rx-bits-per-second"=>"5744844", "tx-bits-per-second"=>"61787", ".tag"=>"2"} >>> '!re' (3) >>> 'rx-bits-per-second=4310298' (26) >>> 'tx-bits-per-second=44242' (24) >>> '.

    • PAGE 197

      API Ruby class 194 {"!re"=>nil, "rx-bits-per-second"=>"3712135", "tx-bits-per-second"=>"28404", ".tag"=>"2"} >>> '!re' (3) >>> 'rx-bits-per-second=4822099' (26) >>> 'tx-bits-per-second=39160' (24) >>> '.tag=2' (6) >>> END-OF SENTENCE {"!re"=>nil, "rx-bits-per-second"=>"4822099", "tx-bits-per-second"=>"39160", ".tag"=>"2"} >>> '!re' (3) >>> 'rx-bits-per-second=4536317' (26) >>> 'tx-bits-per-second=52562' (24) >>> '.

    • PAGE 198

      API Ruby class 195 >>> END-OF SENTENCE Updating DNS settings on multiple devices Imagine I have a list of RouterOS devices that all need primary and secondary DNS settings updated. Here's an example Ruby script to do this: #!/usr/bin/env ruby require 'rubygems' require 'mtik' ## List of devices (hostnames/IPs) to contact: devlist = [ '10.0.0.4', '10.0.0.5', '10.0.0.22', '10.1.44.22', '10.1.44.

    • PAGE 199

      API Ruby class 196 ## for it to complete (either with a '!done' or '!trap' response) before ## executing the callback code block. The call will block (execution of ## this script halts) and wait for the command to finish. Don't use this ## method if you need to handle simultaneous commands to a single device ## over a single API connection. Use an asynchronous calls send_request() ## and wait_for_reply(). mt.

    • PAGE 200

      API Ruby class while children > 1 Process.wait children -= 1 end Output might look a bit like: user@host:~/$ ./dnsupdate.rb 10.0.0.4: Connecting... 10.0.0.4: Update command was sent. 10.0.0.4: Successfully updated DNS servers. 10.0.0.5: Connecting... 10.0.0.5: Update command was sent. 10.0.0.5: Successfully updated DNS servers. ... MORE OUTPUT ... 10.1.44.79: Successfully updated DNS servers.

    • PAGE 201

      API Ruby class One may wonder why not use ?name=foo instead of using the ID parameter. The gem author has discovered that API commands that make changes often do not work even though the API does not respond with an error, unless the object to be changed is directly referenced by .id. In updating or removing users, this appears to be the case.

    • PAGE 202

      API Ruby class >>> '!done' (5) >>> '.tag=1' (6) >>> END-OF SENTENCE <<< '/interface/monitor-traffic' (26) <<< '=interface=ether0' (21) <<< '=.proplist=rx-bits-per-second,tx-bits-per-second' (48) <<< '.tag=2' (6) <<< END-OF-SENTENCE >>> '!re' (3) >>> 'rx-bits-per-second=326944' (25) >>> 'tx-bits-per-second=1175812' (26) >>> '.tag=2' (6) >>> END-OF SENTENCE >>> '!re' (3) >>> 'rx-bits-per-second=84251' (24) >>> 'tx-bits-per-second=444610' (25) >>> '.

    • PAGE 203

      API Ruby class >>> '!re' (3) >>> 'rx-bits-per-second=106012' (25) >>> 'tx-bits-per-second=3952' (23) >>> '.tag=2' (6) >>> END-OF SENTENCE >>> '!re' (3) >>> 'rx-bits-per-second=118867' (25) >>> 'tx-bits-per-second=17370' (24) >>> '.tag=2' (6) >>> END-OF SENTENCE >>> '!re' (3) >>> 'rx-bits-per-second=130582' (25) >>> 'tx-bits-per-second=29941' (24) >>> '.tag=2' (6) >>> END-OF SENTENCE >>> '!re' (3) >>> 'rx-bits-per-second=130582' (25) >>> 'tx-bits-per-second=29941' (24) >>> '.

    • PAGE 204

      API Ruby class >>> '!fatal' (6) >>> 'session terminated on request' (29) >>> END-OF SENTENCE [[{"!re"=>nil, "rx-bits-per-second"=>"326944", "tx-bits-per-second"=>"1175812", ".tag"=>"2"}, {"!re"=>nil, "rx-bits-per-second"=>"84251", "tx-bits-per-second"=>"444610", ".tag"=>"2"}, {"!re"=>nil, "rx-bits-per-second"=>"111604", "tx-bits-per-second"=>"21782", ".tag"=>"2"}, {"!re"=>nil, "rx-bits-per-second"=>"116277", "tx-bits-per-second"=>"681", ".

    • PAGE 205

      Librouteros 202 Librouteros librouteros is a free and open-source library written in C which abstracts the API provided by RouterOS. It was initially written in 2009 by Florian Forster and released under the GNU General Public License (GPL). Features and design goals are: • • • • • Ease of use: The library makes heavy use of callback functions which simplifies memory management. Strict ISO C99 and POSIX.1-2001 conformance. Thread and reentrant-safety. Abstraction from underlying network protocol.

    • PAGE 206

      Librouteros 203 if->running ? "running" : "stopped", if->enabled ? "enabled" : "disabled"); print_interface (if->next); } /* Callback function that is called by "ros_interface" */ static int handle_interfaces (ros_connection_t *c, const ros_interface_t *if, void *user_data) { print_interface (if); return (0); } int main (int argc, char **argv) { ros_connection_t *c; /* Connect to the router */ c = ros_connect ("my-router.example.

    • PAGE 207

      Librouteros 204 References [1] [2] [3] [4] [5] http:/ / verplant. org/ librouteros/ http:/ / verplant. org/ librouteros/ manpages/ librouteros. 3. html http:/ / git. verplant. org/ ?p=routeros-api. git;a=blob;f=src/ ros. c;hb=HEAD http:/ / freshmeat. net/ projects/ librouteros http:/ / directory. fsf. org/ project/ libgcrypt/ API in C This is an implementation of the RouterOS API written in C. This implementation relies on the MD5 digest calculation functions written by Aladdin Enterprises ([1]).

    • PAGE 208

      API in C (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch .

    • PAGE 209

      API in C 206 void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ Pre-requisite MD5 calculation function source file (md5.c) /* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.

    • PAGE 210

      API in C 207 any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library.

    • PAGE 211

      API in C #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define 208 T16 T17 T18 T19 T20 T21 T22 T23 T24 T25 T26 T27 T28 T29 T30 T31 T32 T33 T34 T35 T36 T37 T38 T39 T40 T41 T42 T43 T44 T45 T4

    • PAGE 212

      API in C 209 #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs.

    • PAGE 213

      API in C #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange the bytes in the * right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1.

    • PAGE 214

      API in C SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations.

    • PAGE 215

      API in C 212 SET(d, SET(c, SET(b, SET(a, SET(d, SET(c, SET(b, #undef SET a, d, c, b, a, d, c, b, a, d, c, b, a, d, c, 0, 11, T42); b, 3, 16, T43); a, 6, 23, T44); d, 9, 4, T45); c, 12, 11, T46); b, 15, 16, T47); a, 2, 23, T48); /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti;\ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations.

    • PAGE 216

      API in C 213 pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length.

    • PAGE 217

      API in C 214 { static const md5_byte_t pad[64] 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; md5_byte_t data[8]; int i; = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64.

    • PAGE 218

      API in C 215 struct Sentence **stSentence; int iLength; }; // endianness variable...

    • PAGE 219

      API in C 216 * int fdSock; int iLoginResult; struct Sentence stSentence; struct Block stBlock; fdSock = apiConnect("10.0.0.1", 8728); // attempt login iLoginResult = login(fdSock, "admin", "adminPassword"); if (!iLoginResult) { apiDisconnect(fdSock); printf("Invalid username or password.

    • PAGE 220

      API in C 217 * Returns a socket descriptor ********************************************************************/ int apiConnect(char *szIPaddr, int iPort) { int fdSock; struct sockaddr_in address; int iConnectResult; int iLen; fdSock = socket(AF_INET, SOCK_STREAM, 0); address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr(szIPaddr); address.

    • PAGE 221

      API in C 218 DEBUG ? printf("Closing socket\n") : 0; close(fdSock); } /******************************************************************** * Login to the API * 1 is returned on successful login * 0 is returned on unsuccessful login ********************************************************************/ int login(int fdSock, char *username, char *password) { struct Sentence stReadSentence; struct Sentence stWriteSentence; char *szMD5Challenge; char *szMD5ChallengeBinary; char *szMD5PasswordToSend; char *sz

    • PAGE 222

      API in C 219 // get md5 of the password + challenge concatenation md5_init(&state); md5_append(&state, cNull, 1); md5_append(&state, (const md5_byte_t *)password, strlen(password)); md5_append(&state, (const md5_byte_t *)szMD5ChallengeBinary, strlen(szMD5ChallengeBinary)); md5_finish(&state, digest); // convert this digest to a string representation of the hex values // digest is the binary format of what we want to send // szMD5PasswordToSend is the "string" hex format szMD5PasswordToSend = md5DigestToHe

    • PAGE 223

      API in C 220 * Encode message length and write it out to the socket ********************************************************************/ void writeLen(int fdSock, int iLen) { char *cEncodedLength; // encoded length to send to the api socket char *cLength; // exactly what is in memory at &iLen integer cLength = calloc(sizeof(int), 1); cEncodedLength = calloc(sizeof(int), 1); // set cLength address to be same as iLen cLength = (char *)&iLen; DEBUG ? printf("length of word is %d\n", iLen) : 0; // wri

    • PAGE 224

      API in C 221 { cEncodedLength[0] = cLength[2] | 0xc0; cEncodedLength[1] = cLength[1]; cEncodedLength[2] = cLength[0]; } else { cEncodedLength[0] = cLength[1] | 0xc0; cEncodedLength[1] = cLength[2]; cEncodedLength[2] = cLength[3]; } write (fdSock, cEncodedLength, 3); } // write 4 bytes // this code SHOULD work, but is untested... else if (iLen < 0x10000000) { DEBUG ? printf("iLen < 0x10000000.

    • PAGE 225

      API in C 222 /******************************************************************** * Write a word to the socket ********************************************************************/ void writeWord(int fdSock, char *szWord) { DEBUG ? printf("Word to write is %s\n", szWord) : 0; writeLen(fdSock, strlen(szWord)); write(fdSock, szWord, strlen(szWord)); } /******************************************************************** * Write a sentence (multiple words) to the socket ************************************

    • PAGE 226

      API in C 223 int readLen(int fdSock) { char cFirstChar; // first character read from socket char *cLength; // length of next message to read...will be cast to int at the end int *iLen; // calculated length of next message (Cast to int) cLength = calloc(sizeof(int), 1); DEBUG ? printf("start readLen()\n") : 0; read(fdSock, &cFirstChar, 1); DEBUG ? printf("byte1 = %#x\n", cFirstChar) : 0; // read 4 bytes // this code SHOULD work, but is untested...

    • PAGE 227

      API in C 224 cLength[2] = cFirstChar; cLength[2] &= 0x3f; // mask out the 1st 2 bits read(fdSock, &cLength[1], 1); read(fdSock, &cLength[0], 1); } else { cLength[1] = cFirstChar; cLength[1] &= 0x3f; // mask out the 1st 2 bits read(fdSock, &cLength[2], 1); read(fdSock, &cLength[3], 1); } iLen = (int *)cLength; } // read 2 bytes else if ((cFirstChar & 0x80) == 0x80) { DEBUG ? printf("2-byte encoded length\n") : 0; if (iLittleEndian) { cLength[1] = cFirstChar; cLength[1] &= 0x7f; // mask out the 1st b

    • PAGE 228

      API in C 225 /******************************************************************** * Read a word from the socket * The word that was read is returned as a string ********************************************************************/ char *readWord(int fdSock) { int iLen = readLen(fdSock); int iBytesToRead = 0; int iBytesRead = 0; char *szWord; char *szRetWord; char *szTmpWord; DEBUG ? printf("readWord iLen=%x\n", iLen) : 0; if (iLen > 0) { // allocate memory for strings szRetWord = calloc(sizeof(char), i

    • PAGE 229

      API in C 226 free(szTmpWord); DEBUG ? printf("word = %s\n", szRetWord) : 0; return szRetWord; } else { return NULL; } } /******************************************************************** * Read a sentence from the socket * A Sentence struct is returned ********************************************************************/ struct Sentence readSentence(int fdSock) { struct Sentence stReturnSentence; char *szWord; int i=0; int iReturnLength=0; DEBUG ? printf("readSentence\n") : 0; initializeSentence(&st

    • PAGE 230

      API in C 227 } } // if any errors, get the next sentence if (stReturnSentence.iReturnValue == TRAP || stReturnSentence.iReturnValue == FATAL) { readSentence(fdSock); } if (DEBUG) { for (i=0; i

    • PAGE 231

      API in C 228 return stBlock; } /******************************************************************** * Initialize a new block * Set iLength to 0. ********************************************************************/ void initializeBlock(struct Block *stBlock) { DEBUG ? printf("initializeBlock\n") : 0; stBlock->iLength = 0; } /******************************************************************** * Clear an existing block * Free all sentences in the Block struct and set iLength to 0.

    • PAGE 232

      API in C 229 } /******************************************************************** * Add a sentence to a block * Allocate memory and add a sentence to a Block.

    • PAGE 233

      API in C 230 stSentence->iLength = 0; stSentence->iReturnValue = 0; } /******************************************************************** * Clear an existing sentence ********************************************************************/ void clearSentence(struct Sentence *stSentence) { DEBUG ? printf("initializeSentence\n") : 0; free(stSentence->szSentence); initializeSentence(stSentence); } /******************************************************************** * Add a word to a sentence struct ******

    • PAGE 234

      API in C 231 /******************************************************************** * Add a partial word to a sentence struct...

    • PAGE 235

      API in C 232 ********************************************************************/ char *md5ToBinary(char *szHex) { int di; char cBinWork[3]; char *szReturn; // allocate 16 + 1 bytes for our return string szReturn = malloc((16 + 1) * sizeof *szReturn); // 32 bytes in szHex? if (strlen(szHex) != 32) { return NULL; } for (di=0; di<32; di+=2) { cBinWork[0] = szHex[di]; cBinWork[1] = szHex[di + 1]; cBinWork[2] = 0; DEBUG ? printf("cBinWork = %s\n", cBinWork) : 0; szReturn[di/2] = hexStringToChar(cBinWork

    • PAGE 236

      API in C 233 sprintf(szReturn + di * 2, "%02x", binaryDigest[di]); } return szReturn; } /******************************************************************** * Quick and dirty function to convert hex string to char... * the toConvert string MUST BE 2 characters + null terminated.

    • PAGE 237

      API in C 234 } // now look @ the second car in the 16^0 place if (cToConvert[1] == 'f' || cToConvert[1] == 'F') { iAccumulated += 15; } else if (cToConvert[1] == 'e' || cToConvert[1] == 'E') { iAccumulated += 14; } else if (cToConvert[1] == 'd' || cToConvert[1] == 'D') { iAccumulated += 13; } else if (cToConvert[1] == 'c' || cToConvert[1] == 'C') { iAccumulated += 12; } else if (cToConvert[1] == 'b' || cToConvert[1] == 'B') { iAccumulated += 11; } else if (cToConvert[1] == 'a' || cToConvert[1] == 'A') { i

    • PAGE 238

      API in C 235 { int testWord; char testByte[sizeof(int)]; } endianTest; endianTest.testWord = 1; if (endianTest.testByte[0] == 1) return 1; /* true: little endian */ return 0; /* false: big endian */ } Sample Client (mikrotik-tty.c) #include #include #include #include #include #include #include #include #include "mikrotik-api.

    • PAGE 239

      API in C 236 char *szIPaddr; char *szPort = "8728"; // default port string int iPort; // default port int char *szUsername = "admin"; // default username char *szPassword = ""; // default password int iInteractiveMode = 1; // interactive mode...

    • PAGE 240

      API in C 237 // convert port string to an int iPort = atoi(szPort); } iInteractiveMode ? printf("Connecting to API: %s:%d\n", szIPaddr, iPort) : 0; fdSock = apiConnect(szIPaddr, iPort); iLoginResult = login(fdSock, szUsername, szPassword); if (!iLoginResult) { apiDisconnect(fdSock); iInteractiveMode ? printf("Invalid username or password.

    • PAGE 241

      API in C 238 { writeSentence(fdSock, &stSentence); // receive and print response block from the API stBlock = readBlock(fdSock); printBlock(&stBlock); // clear the sentence clearSentence(&stSentence); } } // if nothing else, simply add the word to the sentence else { addWordToSentence(&stSentence, cWordInput); } } apiDisconnect(fdSock); exit(0); } Notes • The code has been tested with up to 3-byte encoded length. 4 and 5 byte encoded length have not been tested yet.

    • PAGE 242

      API ActionScript 3 class API ActionScript 3 class These are ActionScript 3 classes for working with RouterOS v3 API. You can take it, edit it and use it as you need. NOTE - The API implementation is not yet fully tested with large requests or replies. Please update this section if you have tested this. Also, this ActionScript probably works best in Flash AIR applications (opposed to swf flash files), since they have no security limitations on socket connections. Class // ApiSocket.

    • PAGE 243

      API ActionScript 3 class 240 public function login(u:String, p:String) { doLogin = 1; user = u; password = p; sendRequest("/login"); } public function sendRequest(... outData):void { returnData = new Array(); returnPos = 0; firstRe = 0; gotDone = false; gotTrap = false; gotFatal = false; tag = ""; cmd = outData[0]; returnData[returnPos] = new Object(); for (var i:int = 0; i < outData.length; ++i) { var data:ByteArray = new ByteArray(); var len:uint = outData[i].length; if (len < 0x80) data.

    • PAGE 244

      API ActionScript 3 class 241 0xff); data.writeByte(len & 0xff); } else { data.writeByte(0xF0); data.writeByte((len >> 24) & 0xff); data.writeByte((len >> 16) & 0xff); data.writeByte((len >> 8) & 0xff); data.writeByte(len & 0xff); } writeBytes(data); writeUTFBytes(outData[i]); } writeByte(0); flush(); } private function readResponse():void { var len:int; if (toread == 0) { var len1:uint = readUnsignedByte(); if (len1 == 0) { if (gotDone || gotTrap || gotFatal) { if (doLogin == 1) { if (returnData[0].

    • PAGE 245

      API ActionScript 3 class 242 // Send challenge response sendRequest("/login", "=name=" + user, "=response=00" + MD5.hashBytes(md5)); } } else if (doLogin == 2) { doLogin = 0; dispatchEvent(new ApiEvent(ApiSocket.LOGIN, "", returnData, gotDone ? 'done' : (gotFatal ? 'fatal' : 'trap'))); } else { dispatchEvent(new ApiEvent(ApiSocket.

    • PAGE 246

      API ActionScript 3 class 243 len = ((len1 & 63) << 8) + readUnsignedByte(); } else len = len1; toread = len; } // Calculate how much data of the full length that is available right now var slen:int = bytesAvailable > toread ? toread : bytesAvailable; // Calculate how much data that has to be read later toread = toread > bytesAvailable ? toread bytesAvailable : 0; // Read relevant data var str:String = readUTFBytes(slen); if (toread == 0) { if (str == '!re') { firstRe++; if (firstRe > 1) { returnPos++ retu

    • PAGE 247

      API ActionScript 3 class 244 if (str.substr(0,1) == '!') tag = ""; // Set tag if (str.substr(0,5) == '.tag=') tag = str.substr(5); // Are there more packets available if (bytesAvailable) readResponse(); } } private function socketDataHandler(event:ProgressEvent):void { readResponse(); } } } // ApiEvent.as // // RouterOS API Event class // Author: Håkon Nessjøen of Avelia AS // Date: 2. May 2009 // package { import flash.events.

    • PAGE 248

      API ActionScript 3 class } } Example 1 // Short example that outputs wireless registration-table every second import ApiSocket; import ApiEvent; var sock:ApiSocket; var myTimer:Timer = new Timer(1000); myButton.addEventListener(MouseEvent.CLICK, testAPI); myTimer.addEventListener(TimerEvent.TIMER, timedFunction); function testAPI(evt:MouseEvent):void { sock = new ApiSocket("172.17.1.1", 8728); sock.addEventListener(ApiEvent.RECEIVED, receive); sock.addEventListener(Event.CONNECT, connected); sock.

    • PAGE 249

      API ActionScript 3 class 246 trace(evt.data[i]['mac-address']) trace(" " + evt.data[i]['signal-strength']) } } } API Delphi Client This is implementation of MikroTik RouterOS API Client for Delphi. It supports execution of parallel requests to router and has database-like interface for easy use. Classes RouterOSAPI unit contains definition of two classes which you need to work with API protocol from your Delphi programs.

    • PAGE 250

      API Delphi Client TRosApiResult This class gives you an ability to work with data returned from queries. Each command execution is "isolated" in it's TRosApiResult object, so you can do parallel requests by calling TRosApiClient.Query and receiving several TRosApiResult objects. • property ValueByName[Name: String]: String; default; Returns the value of Name parameter (word in terms of API) in current sentence.

    • PAGE 251

      API Delphi Client if RouterOS.Connect('192.168.0.1', 'admin', 'password') then begin //we are connected successfully end else begin //an error occured; text error message is in LastError property end; Executing queries All queries are done by calling Query function of TRosApiClient. It returns an instance of TRosApiResult, from which all data are fetched. var Res: TRosApiResult; Res := RouterOS.Query(['/system/resource/print'], True); Obtaining the result with GetAll Res := ROS.

    • PAGE 252

      API Delphi Client begin ShowMessage('Done'); ResListen.Free; tmrListen.Enabled := False; Break; end; Memo1.Lines.Add(ResListen['time'] + ': ' + ResListen['message']); until False; end; Downloads and suggestions For downloads and suggestions see forum thread RouterOS API Delphi Client [1] References [1] http:/ / forum. mikrotik. com/ viewtopic. php?f=9& t=31555& start=0 API Delphi This document describes a Delphi class to access RouterOs using API interface.

    • PAGE 253

      API Delphi function send_command(cmd_arr: array of string): integer; Send the strings contained in the array, then send a #0 (example: res:=send_command(['/ppp/active/print','=stats=','=without-paging=']); ). Each send_command increase the command counter of the object. Each !done decrease this counter. You can test if there are pending commands calling the following method: function tr_mkrouter.

    • PAGE 254

      API Delphi The API_STUDIO application I usually develop using mikrotik API and the API STUDIO allow me to test API commands before implementing it in other software. This application is very simple but useful and is a demo for this delphi class (you can also read the simple help in the main form). LOGIN - Insert login informations of router you want to test then print connect. - The objet of class tr_mkrouter is created and open method is called.

    • PAGE 255

      API Delphi 252 DOWNLOADS Please refer to this forum thread for download: API_STUDIO [4]. The download contain all source code and the executable (compiled for i386) of the client to test api commands. References [1] [2] [3] [4] http:/ / www. koders. com/ delphi/ fid5A4F925F646C191A79107D11EDD80DDDF205615E. aspx?s=md5 http:/ / www. ararat. cz/ synapse/ doku. php/ download http:/ / www. koders. com/ delphi/ fidDF48A5F25F06E3C6B1419E0691B806FF60260646. aspx?s=delphi http:/ / forum. mikrotik.

    • PAGE 256

      API in C Sharp 253 } public void Send(string co) { byte[] bajty = Encoding.ASCII.GetBytes(co.ToCharArray()); byte[] velikost = EncodeLength(bajty.Length); connection.Write(velikost, 0, velikost.Length); connection.Write(bajty, 0, bajty.Length); } public void Send(string co, bool endsentence) { byte[] bajty = Encoding.ASCII.GetBytes(co.ToCharArray()); byte[] velikost = EncodeLength(bajty.Length); connection.Write(velikost, 0, velikost.Length); connection.Write(bajty, 0, bajty.Length); connection.

    • PAGE 257

      API in C Sharp 254 else { if (tmp[3] < 0xC0) { int tmpi = BitConverter.ToInt32(new byte[] { (byte)connection.ReadByte(), tmp[3],0,0 }, 0); count = tmpi ^ 0x8000; } else { if (tmp[3] < 0xE0) { tmp[2] = (byte)connection.ReadByte(); int tmpi = BitConverter.ToInt32(new byte[] { (byte)connection.ReadByte(), tmp[2], tmp[3],0 }, 0); count = tmpi ^ 0xC00000; } else { if (tmp[3] < 0xF0) { tmp[2] = (byte)connection.ReadByte(); tmp[1] = (byte)connection.ReadByte(); int tmpi = BitConverter.

    • PAGE 258

      API in C Sharp 255 o += (Char)connection.ReadByte(); } } return output; } byte[] EncodeLength(int delka) { if (delka < 0x80) { byte[] tmp = BitConverter.GetBytes(delka); return new byte[1] { tmp[0] }; } if (delka < 0x4000) { byte[] tmp = BitConverter.GetBytes(delka | 0x8000); return new byte[2] { tmp[1], tmp[0] }; } if (delka < 0x200000) { byte[] tmp = BitConverter.GetBytes(delka | 0xC00000); return new byte[3] { tmp[2], tmp[1], tmp[0] }; } if (delka < 0x10000000) { byte[] tmp = BitConverter.

    • PAGE 259

      API in C Sharp 256 System.Security.Cryptography.MD5 md5; md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); hotovo = md5.ComputeHash(heslo); //Convert encoded bytes back to a 'readable' string string navrat = ""; foreach (byte h in hotovo) { navrat += h.ToString("x2"); } return navrat; } } Example using System.IO; using System.Net.Sockets; class Program { static void Main(string[] args) { MK mikrotik = new MK("your ip here"); if (!mikrotik.Login("username", "password")) { Console.

    • PAGE 260

      API in C Sharp 257 Example 2 Block SMTP port and specific Ip rule by Oguzhan using System.IO; using System.Net.Sockets; class Program { static void Main(string[] args) { string ip = args[0]; MK mikrotik = new MK("your ip here"); if (mikrotik.Login("admin", "P@ssW0rd")) { mikrotik.Send("/ip/firewall/filter/add"); mikrotik.Send("=action=drop"); mikrotik.Send("=chain=forward"); mikrotik.Send("=dst-port=25"); mikrotik.Send("=protocol=tcp"); mikrotik.Send("=protocol=tcp"); mikrotik.Send(String.

    • PAGE 261

      API PHP class API PHP class This is PHP class for working with RouterOS API. You can take it, edit it and use it as you need. NOTE - The class as shown does not work for large replies Author Denis Basta (Denis [dot] Basta [at] gmail [dot] com) Contributors Nick Barnes Ben Menking (ben [at] infotechsc [dot] com) Jeremy Jefferson (http://jeremyj.com) Cristian Deluxe (djcristiandeluxe [at] gmail [dot] com) Changelog 1.

    • PAGE 262

      API PHP class 259 Class

    • PAGE 263

      API PHP class 260 * * @return void */ function encode_length($length) { if ($length < 0x80) { $length = chr($length); } else if ($length < 0x4000) { $length |= 0x8000; $length = chr(($length >> 8) & 0xFF) . chr($length & 0xFF); } else if ($length < 0x200000) { $length |= 0xC00000; $length = chr(($length >> 16) & 0xFF) . chr(($length >> 8) & 0xFF) . chr($length & 0xFF); } else if ($length < 0x10000000) { $length |= 0xE0000000; $length = chr(($length >> 24) & 0xFF) . chr(($length >> 16) & 0xFF) .

    • PAGE 264

      API PHP class 261 $this->write('=response=00' . md5(chr(0) . $password . pack('H*', $MATCHES[0][1]))); $RESPONSE = $this->read(false); if ($RESPONSE[0] == '!done') { $this->connected = true; break; } } } } fclose($this->socket); } sleep($this->delay); } if ($this->connected) $this->debug('Connected...'); else $this->debug('Error...

    • PAGE 265

      API PHP class 262 foreach ($response as $x) { if (in_array($x, array( '!fatal', '!re', '!trap' ))) { if ($x == '!re') { $CURRENT =& $PARSED[]; } else $CURRENT =& $PARSED[$x][]; } else if ($x != '!done') { $MATCHES = array(); if (preg_match_all('/[^=]+/i', $x, $MATCHES)) { if ($MATCHES[0][0] == 'ret') { $singlevalue = $MATCHES[0][1]; } $CURRENT[$MATCHES[0][0]] = (isset($MATCHES[0][1]) ? $MATCHES[0][1] : ''); } } } if (empty($PARSED) && !is_null($singlevalue)) { $PARSED = $singlevalue; } return $PARSED; } e

    • PAGE 266

      API PHP class 263 ))) { if ($x == '!re') $CURRENT =& $PARSED[]; else $CURRENT =& $PARSED[$x][]; } else if ($x != '!done') { $MATCHES = array(); if (preg_match_all('/[^=]+/i', $x, $MATCHES)) { if ($MATCHES[0][0] == 'ret') { $singlevalue = $MATCHES[0][1]; } $CURRENT[$MATCHES[0][0]] = (isset($MATCHES[0][1]) ? $MATCHES[0][1] : ''); } } } foreach ($PARSED as $key => $value) { $PARSED[$key] = $this->array_change_key_name($value); } return $PARSED; if (empty($PARSED) && !is_null($singlevalue)) { $PARSED = $single

    • PAGE 267

      API PHP class 264 return $array_new; } else { return $array; } } /** * Read data from Router OS * * @param boolean $parse Parse the data? default: true * * @return array Array with parsed or unparsed data */ function read($parse = true) { $RESPONSE = array(); $receiveddone = false; while (true) { // Read the first byte of input which gives us some or all of the length // of the remaining reply.

    • PAGE 268

      API PHP class 265 } } else { $LENGTH = $BYTE; } // If we have got more characters to read, read them in. if ($LENGTH > 0) { $_ = ""; $retlen = 0; while ($retlen < $LENGTH) { $toread = $LENGTH - $retlen; $_ .= fread($this->socket, $toread); $retlen = strlen($_); } $RESPONSE[] = $_; $this->debug('>>> [' . $retlen . '/' . $LENGTH . '] bytes read.'); } // If we get a !done, make a note of it.

    • PAGE 269

      API PHP class 266 $com = trim($com); fwrite($this->socket, $this->encode_length(strlen($com)) . $com); $this->debug('<<< [' . strlen($com) . '] ' . $com); } if (gettype($param2) == 'integer') { fwrite($this->socket, $this->encode_length(strlen('.tag=' . $param2)) . '.tag=' . $param2 . chr(0)); $this->debug('<<< [' . strlen('.tag=' . $param2) . '] .tag=' .

    • PAGE 270

      API PHP class Example 1 debug = true; if ($API->connect('111.111.111.111', 'LOGIN', 'PASSWORD')) { $API->write('/interface/getall'); $READ = $API->read(false); $ARRAY = $API->parse_response($READ); print_r($ARRAY); $API->disconnect(); } ?> OR debug = true; if ($API->connect('111.111.111.

    • PAGE 271

      API PHP class debug = true; if ($API->connect('111.111.111.111', 'LOGIN', 'PASSWORD')) { $ARRAY = $API->comm('/interface/getall'); print_r($ARRAY); $API->disconnect(); } ?> Output Array ( [0] => Array ( [.id] => *1 [name] => ether1 [mtu] => 1500 [type] => ether [running] => yes [dynamic] => no [slave] => no [comment] => [disabled] => no ) [1] => Array ( [.

    • PAGE 272

      API PHP class [2] => Array ( [.id] => *3 [name] => ether3 [mtu] => 1500 [type] => ether [running] => yes [dynamic] => no [slave] => no [comment] => ether3 [disabled] => no ) ) Example 2 Thanks a lot for this API, It help me a lot to write my php page for our support team to have access to wireless registration table. $API->write('/interface/wireless/registration-table/print',false); $API->write('=stats='); Output Array ( [0] => Array ( [.

    • PAGE 273

      API PHP class [strength-at-rates] => -73dBm@1Mbps 4m4s640ms,-73dBm@2Mbps 4m58s730ms,-73dBm@5.5Mbps 42s450ms,-73dBm@11Mbps 90ms [tx-ccq] => 91 [p-throughput] => 5861 [ack-timeout] => 31 [last-ip] => 192.168.0.220 [802.1x-port-enabled] => true [wmm-enabled] => false ) [1] => Array ( ... ) ... ) Example 3 Adding vpn user $API->comm("/ppp/secret/add", array( "name" => "user", "password" => "pass", "remote-address" => "172.16.1.

    • PAGE 274

      API PHP class $API->write('~active-address~"1.1."'); $ARRAY = $API->read(); print_r($ARRAY); Returns a number with leases MikroTik for Mac There are several possibilities to use Winbox on Apple Mac computers: 1. Winbox wrapper [1] v1.4 by Ivan Tiukov (ivan@tiukov.com) 2. Winbox in Winebottler by SomniusX (hellasproject.com) 3.

    • PAGE 275

      MikroTik for Mac MacPorts and wine If you cannot get Darwine to work, you can install Wine using Mac Ports at http:/ / guide. macports. org/ Once MacPorts is installed open terminal and type the following: # sudo port install wine This will take quite a while to download and compile all the relevant packages needed. When finished you can type the following into a terminal window to start Winbox. I'm assuming here that you copied a version of winbox.

    • PAGE 276

      MikroTik for Mac 273 Making Home/End/PgUp/PgDown work in Terminal.app It really annoys me that by default you have no Home/End functionality in terminal.app. Editing large RouterOS scripts or just trying to modify already typed command can become a tedious task without these. So here is a quick guide how to fix this. 1. 2. 3. 4. :: :: :: :: Open Terminal.

    • PAGE 277

      Assorted examples Assorted examples • • • • • • • • • • • • • • • Automated Backups Automatic Backup with Centralized Storage - By Ashish Patel Bash scripts for Linux/Mysql/Freeradius/PPPoE Booting RouterOS from USB drives Centralized Authentication for Hotspot user Change MAC address of VLAN interface Configuring RouterOS to work with EVDO/3G PCMCIA card Configuring RouterOS to work with EVDO/3G USB Modem Event WiFi By EITL-India External Squid Box with No Limit Cache HIT Object ROS 2.

    • PAGE 278

      Article Sources and Contributors Article Sources and Contributors MikroTik RouterOS Source: http://wiki.mikrotik.com/index.php?oldid=22355 Contributors: Dzintars, Eugene, HarvSki, Lastguru, Marisb, MediaWiki default, Normis, Paulhoff, Rainer Wieland, Rieks, Route, WikiSysop Hardware Source: http://wiki.mikrotik.com/index.php?oldid=20098 Contributors: Brianlewis, Cmit, DonGould, Marisb, Normis, Rajeshrouthu, Sdischer Supported Hardware Source: http://wiki.mikrotik.com/index.

    • PAGE 279

      Article Sources and Contributors MikroTik for Mac Source: http://wiki.mikrotik.com/index.php?oldid=22848 Contributors: Chenull, Donie, Eugene, Henkk78, Jeffsporos, Macsrwe, Myrrhman, Ni3ls, Normis, SergejsB, SomniusX, Tecpro, Tiukov, Zee Assorted examples Source: http://wiki.mikrotik.com/index.

    • PAGE 280

      Image Sources, Licenses and Contributors Image Sources, Licenses and Contributors Image:Shot.jpg Source: http://wiki.mikrotik.com/index.php?title=File:Shot.jpg License: unknown Contributors: Normis Image:Icon-note.png Source: http://wiki.mikrotik.com/index.php?title=File:Icon-note.png License: unknown Contributors: Marisb, Route Image:Version.png Source: http://wiki.mikrotik.com/index.php?title=File:Version.png License: unknown Contributors: Normis File:TorMikrotikDiagram.jpg Source: http://wiki.mikrotik.