User Tools

Site Tools


ubus (OpenWrt micro bus architecture)

To provide communication between various daemons and applications in OpenWrt an ubus project has been developed. It consists of few parts including daemon, library and some extra helpers.

The heart of this project is ubusd daemon. It provides interface for other daemons to register themselves as well as sending messages. For those curious, this interface is implemented using Unix socket and it uses TLV (type-length-value) messages.

To simplify development of software using ubus (connecting to it) a library called libubus has been created.

Every daemon registers set of own paths under specific namespace. Every path can provide multiple procedures with various amount of arguments. Procedures can reply with a message.

The code is published under LGPL 2.1 and can be found via git at It's included in OpenWrt since r28499.

Command-line ubus tool

The ubus command line tool allows to interact with the ubusd server (with all currently registered services). It's useful for investigating/debugging registered namespaces as well as writing shell scripts. For calling procedures with parameters and returning responses it uses user-friendly JSON format. Below is an explanation of its commands.


By default, list all namespaces registered with the RPC server:

root@uplink:~# ubus list

If invoked with -v, additionally the procedures and their argument signatures are dumped in addition to the namespace path:

root@uplink:~# ubus -v list network.interface.lan
'network.interface.lan' @099f0c8b
	"up": {  }
	"down": {  }
	"status": {  }
	"prepare": {  }
	"add_device": { "name": "String" }
	"remove_device": { "name": "String" }
	"notify_proto": {  }
	"remove": {  }
	"set_data": {  }


Calls a given procedure within a given namespace and passes given message to it:

root@uplink:~# ubus call network.interface.wan status
	"up": true,
	"pending": false,
	"available": true,
	"autostart": true,
	"uptime": 86017,
	"l3_device": "eth1",
	"device": "eth1",
	"address": [
			"address": "",
			"mask": 21
	"route": [
			"target": "",
			"mask": 0,
			"nexthop": ""
	"data": {

The message argument must be a valid JSON string, with keys and values set according to the function signature:

root@uplink:~# ubus call network.device status '{ "name": "eth0" }'
	"type": "Network device",
	"up": true,
	"link": true,
	"mtu": 1500,
	"macaddr": "c6:3d:c7:90:aa:da",
	"txqueuelen": 1000,
	"statistics": {
		"collisions": 0,
		"rx_frame_errors": 0,
		"tx_compressed": 0,
		"multicast": 0,
		"rx_length_errors": 0,
		"tx_dropped": 0,
		"rx_bytes": 0,
		"rx_missed_errors": 0,
		"tx_errors": 0,
		"rx_compressed": 0,
		"rx_over_errors": 0,
		"tx_fifo_errors": 0,
		"rx_crc_errors": 0,
		"rx_packets": 0,
		"tx_heartbeat_errors": 0,
		"rx_dropped": 0,
		"tx_aborted_errors": 0,
		"tx_packets": 184546,
		"rx_errors": 0,
		"tx_bytes": 17409452,
		"tx_window_errors": 0,
		"rx_fifo_errors": 0,
		"tx_carrier_errors": 0


Setup a listening socket and observe incoming events:

root@uplink:~# ubus listen &
root@uplink:~# ubus call network.interface.wan down
{ "network.interface": { "action": "ifdown", "interface": "wan" } }
root@uplink:~# ubus call network.interface.wan up
{ "network.interface": { "action": "ifup", "interface": "wan" } }
{ "network.interface": { "action": "ifdown", "interface": "he" } }
{ "network.interface": { "action": "ifdown", "interface": "v6" } }
{ "network.interface": { "action": "ifup", "interface": "he" } }
{ "network.interface": { "action": "ifup", "interface": "v6" } }


Send an event notification:

root@uplink:~# ubus listen &
root@uplink:~# ubus send foo '{ "bar": "baz" }'
{ "foo": { "bar": "baz" } }

Access to ubus over HTTP

There is an uhttpd plugin called uhttpd-mod-ubus that allows ubus calls using HTTP protocol. Requests have to be send to the /ubus URL (unless changed) using POST method. This interface uses jsonrpc v2.0 There are a few steps that you will need to understand. (Documentation written while using BarrierBreaker r40831, ymmv)


While logged into via ssh, you have direct, full access to ubus. When you're accessing the /ubus url in uhttpd however, uhttpd runs "ubus call session access '{ "ubus_rpc_session": "xxxxx, "object": "requested-object", "function": "requested-method" }' and whoever is providing the ubus session.* namespace is in charge of implementing the ACL. This happens to be rpcd at the moment, with the http-json interface, for friendly operation with browser code, but this is just one possible implemementation. Because we're using rpcd to implement the ACLs at this time, this allows/requires (depending on your point of view) ACLs to be configured in /usr/share/rpcd/acl.d/*.json. The names of the files in /usr/share/rpcd/acl.d/*.json don't matter, but the top level keys define roles. The default acl, listed below, only defines the login methods, so you can login, but you still wouldn't be able to do anything.

        "unauthenticated": {
                "description": "Access controls for unauthenticated requests",
                "read": {
                        "ubus": {
                                "session": [ "access", "login" ]

An example of a complicated ACL, allowing quite fine grained access to different ubus modules and methods is available in the Luci2 project

An example of a "security is for suckers" config, where a "superuser" ACL group is defined, allowing unrestricted access to everything, is shown below. (This illustrates the usage of '*' definitions in the ACLs, but keep reading for better examples) Placing this file in /usr/share/rpcd/acl.d/superuser.json will help you move forward to the next steps.

        "superuser": {
                "description": "Super user access role",
                "read": {
                        "ubus": {
                                "*": [ "*" ]
                        "uci": [ "*" ]
                "write": {
                        "ubus": {
                                "*": [ "*" ]
                        "uci": [ "*" ]

Below is an example of an ACL definition that only allows access to some specific ubus modules, rather than unrestricted access to everything.

        "lesssuperuser": {
                "description": "not quite as super user",
                "read": {
                        "ubus": {
                                "file": [ "*" ],
                                "log": [ "*" ],
                                "service": [ "*" ],
                "write": {
                        "ubus": {
                                "file": [ "*" ],
                                "log": [ "*" ],
                                "service": [ "*" ],

Note: Before we leave this section, you may have noticed that there's both a "ubus" and a "uci" section, even though ubus has a uci method. The uci: scope is used for the uci api provided by rpcd to allow defining per-file permissions because using the ubus scope you can only say "uci set" is allowed or not allowed but not specify that it is allowed to e.g. modify /e/c/system but not /e/c/network If your application/ACL doesn't need UCI access, you can just leave out the UCI section altogether.


Now that we have an ACL that allows operations beyond just logging in, we can actually try this out. As mentioned, rpcd is handling this, so you need an entry in /etc/config/rpcd

config login
	option username 'root'
	option password '$p$root'
	list read '*'
	list write '*'
The $p magic means to look in /etc/shadow and the $root part means to use the password for the root user in that file. The list of read and write sections, those map acl roles to user accounts. You can also use $1$<hash>which is a "crypt()" hash, using SHA1, exactly as used in /etc/shadow. You can generate these with, eg, uhttpd -m secret

To login and receive a session id:

$ curl -d '{ "jsonrpc": "2.0", "id": 1, "method": "call", "params": [ "00000000000000000000000000000000", "session", "login", { "username": "root", "password": "secret"  } ] }'  http://your.server.ip/ubus


The sessionid "00000000000000000000000000000000" (32 zeros) is a special null-session which just has enough access rights for the session.login ubus call. A session has a timeout, that is specified when you login, but has a default. You can request a longer timeout in your initial login call, with a "timeout" key in the login parameters section.

If you ever receive a response like, {"jsonrpc":"2.0","id":1,"result":[6]} That is a valid jsonrpc response, 6 is the ubus code for UBUS_STATUS_PERMISSION_DENIED (you'll get this if you try and login before setting up the "superuser" file, or any file that gives you anymore rights than just being allowed to attempt logins.

To list all active sessions, try ubus call session list

Session management

A session is automatically renewned on every use. There are plans to use these sessions even for luci1, but at present, if you use this interface in a luci1 environment, you'll need to manage sessions yourself.

Actually making calls

Now that you have a ubus_rpc_session you can make calls, based on your ACLs and the available ubus services. ubus list -v is your primary documentation on what can be done, but see the rest of this page for more information. For example, ubus list file -v returns

'file' @24a6bd4a

The json container format is:

{ "jsonrpc": "2.0",
  "id": <unique-id-to-identify-request>, 
  "method": "call",
  "params": [
             <ubus_rpc_session>, <ubus_object>, <ubus_method>, 
             { <ubus_arguments> }
The "id" key is merely echo'ed by the server, so it needs not be strictly unique, it's mainly intended for client software to easily correlate responses to previously made requests. It's type is either a string or a number, so it can be an sha1 hash, md5 sum, sequence counter, unix timestamp, ….

An example request to read a file would be:

$ curl -d '{ "jsonrpc": "2.0", "id": 1, "method": "call", "params": [ "7cba69a942c0e9db1eb7982cd91f3a48", "file", "read", { "path": "/tmp/hello.karl" } ] }'  http://eg-134867.local/ubus
{"jsonrpc":"2.0","id":1,"result":[0,{"data":"this is the contents of a file\n"}]}

Lua module for ubus

This is even possible to use ubus in lua scripts. Of course it's not possible to use native libraries directly in lua, so an extra module has been created. It's simply called ubus and is a simple interface between lua scripts and the ubus (it uses libubus internally).

Load module

require "ubus"

Establish connection

local conn = ubus.connect()
if not conn then
    error("Failed to connect to ubusd")

Optional arguments to connect() are a path to use for sockets (pass nil to use the default) and a per call timeout value (in milliseconds)

local conn = ubus.connect(nil, 500) -- default socket path, 500ms per call timeout

Iterate all namespaces and procedures

local namespaces = conn:objects()
for i, n in ipairs(namespaces) do
    print("namespace=" .. n)
    local signatures = conn:signatures(n)
    for p, s in pairs(signatures) do
        print("\tprocedure=" .. p)
        for k, v in pairs(s) do
            print("\t\tattribute=" .. k .. " type=" .. v)

Call a procedure

local status = conn:call("network.device", "status", { name = "eth0" })
for k, v in pairs(status) do
    print("key=" .. k .. " value=" .. tostring(v))

Close connection


Namespaces & Procedures

As explained earlier, there can be many different daemons (services) registered in ubus. Below you will find a list of the most common projects with namespaces, paths and procedures they provide.


DESIGN document at repo of netifd

Path Procedure Signature Description
network restart { } Restart the network, reconfigures all interfaces
network reload { } Reload the network, reconfigure as needed
network.device status { "name": "ifname" } Dump status of given network device ifname
network.device set_state { "name": "ifname", "defer": deferred } Defer or ready the given network device ifname, depending on the boolean value deferred up { } Bring interface name up down { } Bring interface name down status { } Dump status of interface name prepare { } Prepare setup of interface name add_device { "name": "ifname" } Add network device ifname to interface name (e.g. for bridges: brctl addif br-name ifname) remove_device { "name": "ifname" } Remove network device ifname from interface name (e.g. for bridges: brctl delif br-name ifname) remove { } Remove interface name (?)


Project rpcd is set of small plugins providing sets of ubus procedures in separated namespaces. These plugins are not strictly related to any particular software (like netifd or dhcp) so it wasn't worth it to implement them as separated projects. To enable these plugins rpcd should be available or installed via opkg. After installing remember to enable and start the service via /etc/init.d/rpcd enable and /etc/init.d/rpcd start .

Path Procedure Signature Description
file read ? ?
file write ? ?
file list ? ?
file stat ? ?
file exec ? ?

Path Procedure Signature Description
iwinfo info ? ?
iwinfo scan ? ?
iwinfo assoclist ? ?
iwinfo freqlist ? ?
iwinfo txpowerlist ? ?
iwinfo countrylist ? ?
Path Procedure Signature Description
session list { "session": "sid" } Dump session specified by sid, if no ID is given, dump all sessions
session create { "timeout": timeout } Create a new session and return its ID, set the session timeout to timeout
session grant { "session": "sid", "objects": [ [ "path", "func" ], … ] } Within the session identified by sid grant access to all specified procedures func in the namespace path listed in the objects array
session revoke { "session": "sid", "objects": [ [ "path", "func" ], … ] } Within the session identified by sid revoke access to all specified procedures func in the namespace path listed in the objects array. If objects is unset, revoke all access
session access ? ?
session set { "session": "sid", "values": { "key": value, … } } Within the session identified by sid store the given arbitrary values under their corresponding keys specified in the values object
session get { "session": "sid", "keys": [ "key", … ] } Within the session identified by sid retrieve all values associated with the given keys listed in the keys array. If the key array is unset, dump all key/value pairs
session unset { "session": "sid", "keys": [ "key", … ] } Within the session identified by sid unset all keys listed in the keys array. If the key list is unset, clear all keys
session destroy { "session": "sid" } Terminate the session identified by the given ID sid
session login ? ?
Path Procedure Signature Description
uci get { "package": "package", "section": "sname", "type": "type", "option": "oname" }

Return the requested uci value(s), all arguments are optional.

  1. When called without argument or with empty object: return an array of package names in the packages field
  2. When called with package set: return an object containing all sections containing all options in a field named after the package
  3. When called with package and type set: return an object containing all sections of type type containing all options in a field named after the package
  4. When called with package and sname set: return an object containing all options of the section in a field named after the section
  5. When called with package and type and oname set: return an object containing the value of each option named oname within a section of type type in a field named after the matched section
  6. When called with package and sname and oname set: return the result string in a field named oname in case of options or an array of result strings in a field named oname in case of list options

Return messages:

  1. { "packages": [ "package1", … ] }
  2. { "package": { "sname1": { ".type": "type1", "option1": "value1", "option2": [ "value2.1", … ], … }, … } }
  3. { "package": { "sname1": { ".type": "type", "option1": "value1", "option2": [ "value2.1", … ], … }, … } }
  4. { "sname": { ".type": "type", "option1": "value1", "option2": [ "value2.1", … ], … } }
  5. { "sectionname1": "value1", "sectionname2": [ "value2.1", … ], … }
    1. { "oname": "value1" }
    2. { "oname": [ "value1.1", … ] }
uci set { "package": "package", "section": "sname", "option": "oname", "value": "value" }

Set the given value(s), the option argument is optional.

  1. When called with package and sname and value set: add a new section sname in package and set it to the type given in value
  2. When called with package and sname, oname and value set:
    1. If value is of type array: set strings in the value array as list option oname
    2. If value is of type string: set value as normal option oname

The call does not produce any data, instead it returns with the following status codes:

  1. If there already is a section called sname: UBUS_STATUS_INVALID_ARGUMENT else: UBUS_STATUS_OK
  2. If there is no section sname or if value is neither a string nor an array: UBUS_STATUS_INVALID_ARGUMENT else: UBUS_STATUS_OK
uci add { "package": "package", "type": "type" }

Add new anonymous section of given type.

  1. When called with package and type set: Add a new anonymous section of type type.

Return message:

  1. { "section": "sectionname" }
uci delete { "package": "package", "section": "sname", "type": "type", "option": "oname" }

Delete the given value(s) or section(s), the option and type arguments are optional.

  1. When called with package and type set: delete all sections of type type in package
  2. When called with package and sname set: delete the section named sname in package
  3. When called with package, type and oname set: delete the option named oname within each section of type type in package
  4. When called with package, sname and oname set: delete the option named oname in section sname of package

The call does not result in any data, instead it returns the following status codes:

  1. If no section of type type was found: UBUS_STATUS_NOT_FOUND else: UBUS_STATUS_OK
  2. If no section named sname was found: UBUS_STATUS_NOT_FOUND else: UBUS_STATUS_OK
  3. If no options named oname within sections of type type where found: UBUS_STATUS_NOT_FOUND else: UBUS_STATUS_OK
  4. If the option named oname within named section sname was not found: UBUS_STATUS_NOT_FOUND else: UBUS_STATUS_OK

Example code snippets


. /usr/share/libubox/

WANDEV="$(uci get network.wan.ifname)"

json_load "$(devstatus $WANDEV)"

json_get_var var1 speed
json_get_var var2 link

echo "Speed: $var1"
echo "Link: $var2"

doc/techref/ubus.txt · Last modified: 2016/07/01 09:33 by zajec