RouterOS API library for C

Sometimes I get these ideas in my head that I just have to write out.

librouteros is one of those.

If you need to check data or do periodic updates, or similar stuff to a RouterOS device from a script or program, you should use the RouterOS API. This is an open API that you can read about on Mikrotiks wiki pages.

Earlier I have made a RouterOS API implementation in Flash ActionScript3. You can find it on the wiki page I just linked to. This time I felt that all C implementations of the routeros-api has been either over complicated, or too simple. And by complicated, I don’t mean in a good way. They are just hard to use without any special gain for being complicated.

I set up to create a simple API to talk to the router. At first I just created a “proof of concept” with blocking sockets. But my initial idea was to create a library that supported “asyncronous” data from the devices using the “.tag” support in the API. So now, the current version of my library supports using callbacks to handle scheduled commands. This means that you can send “monitor” commands, and automatically receive updates from the routeros device just when data changes.

Since I made this in two stages, I decided to leave the support for the “simple” interface which uses blocking sockets, for those who just want to send a simple command and read the response. But for the more advanced user, you can use the non-blocking interface where you define callbacks for the commands you send, and handle them asynchronously inside your own run-loop.

int main(int argc, char **argv) {
	struct sockaddr_in address;
	int len;
 
	if (argc < 4) {
		fprintf(stderr, "Usage: %s   \n", argv[0]);
		return 1;
	}
 
	conn = ros_connect(argv[1], ROS_PORT); 
	if (conn == NULL) {
		fprintf(stderr, "Error connecting to %s: %s\n", argv[1], strerror(errno));
		return 1;
	}
 
	if (ros_login(conn, argv[2], argv[3])) {
		struct ros_result *res;
 
		printf("Interfaces:\n");
 
		res = ros_send_command_wait(conn, "/interface/print", "=stats", ".tag=kake", NULL);
		while (res && res->re) {
 
			printf("  %20s  %20s  %20s  %20s\n", ros_get(res, "=name"), ros_get(res, "=type"), ros_get(res, "=rx-byte"), ros_get(res, "=tx-byte"));			
 
			ros_result_free(res);
			res = ros_read_packet(conn);
		}
		ros_result_free(res);
 
		ros_disconnect(conn);
	} else {
		fprintf(stderr, "Error logging in\n");
		return 1;
	}
 
	return 0;
}

Above you see a simple example of how to fetch info from the /interface/print command. This is using the simple blocking portion of the library.

For the non-blocking, main-loop based example of fetching the same information, there is a lot more code, so instead of putting it inline here, you can check the example file in the git repository. This example is a bit larger because I am also setting up the mainloop using select(), etc. But if you really go through the code, it’s not that much more code. It’s quite simple to use.
The callback “handleInterface” gets called when each “sentence” is returned from the command. I have added a “task” counter, to exit the program immediatly after all the interesting data is retrieved.

I have also created two ways of generating a “sentence” to send to the router-os device. As you see in the Mikrotik API specification, you need to generate a “sentence” using a series of “words” (read: key value pairs).

The simplest way, when you have a static amount of words you want to send, you can use the simple variable-parameter function ros_send_command_cb(), specifying all your words in a row, finishing with the last parameter to the command being NULL.

ros_send_command_cb(conn, handleInterface, "/interface/print", "=stats", NULL);

But if the amount of parameters is dynamic, you should rather build the sentence one word at a time, like this:

/* Build the sentence */
sentence = ros_sentence_new();
ros_sentence_add(sentence, "/system/resource/print");
ros_sentence_add(sentence, "=.proplist=uptime,cpu-load");
 
/* Send the sentence */
ros_send_sentence_cb(conn, handleUptime, sentence);
/* Free the sentence */
ros_sentence_free(sentence);

In this example, you could easily have a if() scope around the last word (the one with .proplist), making it optional, etc. But remember to free the sentence using ros_sentence_free(sentence) after you have sent the sentence. Look at the example, or any of the other example files, if you don’t understand my poor English.

I have tested this library with valgrind all the way under development and it is clean from memory leaks as far as valgrind knows. And it should be completely thread safe. I have also compiled and tested the library on Windows XP, Windows CE, Pocket PC, Mac OSX (console and app) and iPhone IOS. There is no need for modifications of the code base.

Please leave a comment or mail if you like this library, or decide to use it in a project.

Last commits on librouteros-api on GitHub

Posted December 22nd, 2012 in Uncategorized.

Leave a response: