浅谈OpenWRT的netifd和ubus

1、netifd简介

1.1 netifd简介

  OpenWRT为网络接口管理和配置创建了一个单独的项目——netifd。不同于其他发行版上针对同样问题领域采用的方案,netifd的目标是更适合在嵌入式家庭网关上使用,因此也具有一些特点。

1.2 netifd包含下面这些组件

程序
组件
Shell脚本/sbin/ifup,/sbin/ifdown,/sbin/ifstatus,/sbin/devstatus
init.d脚本/etc/init.d/network
hotplug2脚本/etc/hotplug.d/iface/00-netstate, /etc/hotplug.d/iface/10-sysctl
udhcpc脚本/usr/share/udhcpc/default.script
netifd守护进程/sbin/netifd

1.3 netifd组件分析

下面对这些组件,逐一进行分析,希望以此来理解netifd的基本工作机制。

1.3.1 ifup和ifdown脚本

  ifup和ifdown脚本都被存放在/sbin目录下,ifdown实际上是指向ifup的符号链接。所以,这两个脚本实际上是同一个脚本文件。查看过程如下所示:

root@OpenWrt:~# ls -l /sbin/ifup
-rwxrwxr-x    1 root     root          1286 Sep  1 09:27 /sbin/ifup
root@OpenWrt:~# 
root@OpenWrt:~# ls -l /sbin/ifdown 
lrwxrwxrwx    1 root     root             4 Sep  1 09:27 /sbin/ifdown -> ifup
root@OpenWrt:~#

  ifup和ifdown的语法格式如下所示:

ifup [-a] [-w] <interface>
ifdown  [-a] [-w] <interface>

下面是对ifup/ifdown命令选项的简介。

选项作用
-a对所有接口均执行相同的操作,此时interface被忽略.此参数默认为false
-w是否执行wifi up操作。如果此参数被指定,则wifi up操作不会被执行。如果未指定,则在ifup的时候,wifi up会被执行
interface指定down/up操作的目标接口

  ifup的脚本里面,关于wifi的操作是通过/sbin/wifi这个脚本来执行的,所以在这里暂时不讨论。关于普通的ifdown或ifup操作,这个脚本都是通过ubus命令来实现的。在/sbin/ifup脚本中定义了一个if_call()函数,具体如下所示:

if_call()
{
        local interface="$1"
        for mode in $modes; do
                ubus call network.interface $mode "{ \"interface\" : \"$interface\" }"
        done
}

  可以看到这个函数有一个名为interface的参数,然后还使用了一个全局参数。而参数modes则被定义在ifup脚本里面,具体如下所示:

case "$0" in
        *ifdown) modes=down;;
        *ifup)
                modes="down up"
                setup_wifi=1
        ;;
        *) echo "Invalid command: $0";;
esac

所以,当执行ifdown lan命令时,对应的ubus命令为”ubus call network.interface.lan down。执行ifup lan命令时,对应的ubus命令就有两条。先执行ubus call network.interface.lan down命令,然后再执行ubus call network.interface.lan up命令。

2、ubus和ubusd简介

  OpenWRT提供了一个ubus系统,它类似于桌面Linux系统中的dbus。主要目的也是提供系统级的IPC和RPC。ubus在设计理念上与dbus基本保持一致,区别在于简化的API和简练的模型,以适应于嵌入式路由器的特殊环境。

2.1 ubus的组件

  OpenWRT的ubus组主要由ubus和ubus组成,具体见下表。

组件
作用
ubusd这个是ubus系统的后台进程,负责注册unix domain socket,分派ubus消息和事件等。
ubus这是一个CLI utility,可以通过它访问ubus系统。
ubus的各个应用程序这些应用程序可以在ubus系统中注册RPC接口,提供相应的服务。而其他程序可以通过使用这些接口来访问这些服务。

  ubus的帮助信息如下所示:

Usage: ubus [<options>] <command> [arguments...]
Options:
 -s <socket>:           Set the unix domain socket to connect to
 -t <timeout>:          Set the timeout (in seconds) for a command to complete
 -S:                    Use simplified output (for scripts)
 -v:                    More verbose output
 -m <type>:             (for monitor): include a specific message type
                        (can be used more than once)
 -M <r|t>               (for monitor): only capture received or transmitted traffic

Commands:
 - list [<path>]                        List objects
 - call <path> <method> [<message>]     Call an object method
 - subscribe <path> [<path>...] Subscribe to object(s) notifications
 - listen [<path>...]                   Listen for events
 - send <type> [<message>]              Send an event
 - wait_for <object> [<object>...]      Wait for multiple objects to appear on ubus
 - monitor                              Monitor ubus traffic

  ubus提供了6种子命令,其中常用的子命令有4种,分别为list、call、listen以及send。通过使用这四种子命令,我们就可以轻松的访问注册到ubus系统中的服务了。

2.2 netifd的ubus RPC接口

在OpenWRT中使用如下命令可以查看netifd在ubus系统中注册了的所有对象。具体演示过程如下所示:

'dhcp' @f7f1dc29
        "ipv4leases":{}
        "ipv6leases":{}
        "add_lease":{"ip":"String","mac":"String","duid":"String","hostid":"String","leasetime":"String","name":"String"}
'dnsmasq' @9143135f
        "metrics":{}
'file' @8925e8d9
        "read":{"path":"String","base64":"Boolean","ubus_rpc_session":"String"}
        "write":{"path":"String","data":"String","append":"Boolean","mode":"Integer","base64":"Boolean","ubus_rpc_session"}
        "list":{"path":"String","ubus_rpc_session":"String"}
        "stat":{"path":"String","ubus_rpc_session":"String"}
        "md5":{"path":"String","ubus_rpc_session":"String"}
        "remove":{"path":"String","ubus_rpc_session":"String"}
        "exec":{"command":"String","params":"Array","env":"Table","ubus_rpc_session":"String"}
'hostapd' @4e9102ab
        "config_add":{"iface":"String","config":"String"}
        "config_remove":{"iface":"String"}
'hotplug.dhcp' @1c0f1fef
        "call":{"env":"Array"}
'hotplug.ieee80211' @270baa6c
        "call":{"env":"Array"}
'hotplug.iface' @d8ed4c69
        "call":{"env":"Array"}
'hotplug.neigh' @cbb19dd5
        "call":{"env":"Array"}
'hotplug.net' @eea39d9a
        "call":{"env":"Array"}
'hotplug.ntp' @7a836d9d
        "call":{"env":"Array"}
'hotplug.tftp' @b0f9191c
        "call":{"env":"Array"}
'hotplug.usb' @d4aee266
        "call":{"env":"Array"}
'iwinfo' @d97cdc95
        "devices":{}
        "info":{"device":"String"}
        "scan":{"device":"String"}
        "assoclist":{"device":"String","mac":"String"}
        "freqlist":{"device":"String"}
        "txpowerlist":{"device":"String"}
        "countrylist":{"device":"String"}
        "survey":{"device":"String"}
        "phyname":{"section":"String"}
'log' @11411906
        "read":{"lines":"Integer","stream":"Boolean","oneshot":"Boolean"}
        "write":{"event":"String"}
'luci' @bbc5ddfd
        "getMountPoints":{}
        "getFeatures":{}
        "setBlockDetect":{}
        "getSwconfigFeatures":{"switch":"String"}
        "setPassword":{"username":"String","password":"String"}
        "getConntrackHelpers":{}
        "getUSBDevices":{}
        "getInitList":{"name":"String"}
        "getProcessList":{}
        "getBlockDevices":{}
        "getRealtimeStats":{"device":"String","mode":"String"}
        "getSwconfigPortState":{"switch":"String"}
        "getLEDs":{}
        "getConntrackList":{}
        "setLocaltime":{"localtime":"Integer"}
        "getTimezones":{}
        "setInitAction":{"name":"String","action":"String"}
        "getLocaltime":{}
'luci-rpc' @a0a88098
        "getNetworkDevices":{}
        "getWirelessDevices":{}
        "getHostHints":{}
        "getDUIDHints":{}
        "getBoardJSON":{}
        "getDHCPLeases":{"family":"Integer"}
'network' @32f01ebb
        "restart":{}
        "reload":{}
        "add_host_route":{"target":"String","v6":"Boolean","interface":"String"}
        "get_proto_handlers":{}
        "add_dynamic":{"name":"String"}
        "netns_updown":{"jail":"String","pid":"Integer","start":"Boolean"}
'network.device' @75f4ab54
        "status":{"name":"String"}
        "set_alias":{"alias":"Array","device":"String"}
        "set_state":{"name":"String","defer":"Boolean","auth_status":"Boolean"}
'network.interface' @0e4d1c10
        "up":{}
        "down":{}
        "renew":{}
        "status":{}
        "prepare":{}
        "dump":{}
        "add_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "remove_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "notify_proto":{}
        "remove":{}
        "set_data":{}
'network.interface.lan' @d822397a
        "up":{}
        "down":{}
        "renew":{}
        "status":{}
        "prepare":{}
        "dump":{}
        "add_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "remove_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "notify_proto":{}
        "remove":{}
        "set_data":{}
'network.interface.loopback' @afbe04c1
        "up":{}
        "down":{}
        "renew":{}
        "status":{}
        "prepare":{}
        "dump":{}
        "add_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "remove_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "notify_proto":{}
        "remove":{}
        "set_data":{}
'network.interface.wan' @ec3fd869
        "up":{}
        "down":{}
        "renew":{}
        "status":{}
        "prepare":{}
        "dump":{}
        "add_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "remove_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "notify_proto":{}
        "remove":{}
        "set_data":{}
'network.interface.wan6' @43cb9475
        "up":{}
        "down":{}
        "renew":{}
        "status":{}
        "prepare":{}
        "dump":{}
        "add_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "remove_device":{"name":"String","link-ext":"Boolean","vlan":"Array"}
        "notify_proto":{}
        "remove":{}
        "set_data":{}
'network.rrdns' @01950e10
        "lookup":{"addrs":"Array","timeout":"Integer","server":"String","port":"(unknown)","limit":"Integer"}
'network.wireless' @99c526f5
        "up":{}
        "down":{}
        "reconf":{}
        "status":{}
        "notify":{}
        "get_validate":{}
'rc' @dcd62cdb
        "list":{}
        "init":{"name":"String","action":"String"}
'service' @79a2b8b2
        "set":{"name":"String","script":"String","instances":"Table","triggers":"Array","validate":"Array","autostart":"Bo}
        "add":{"name":"String","script":"String","instances":"Table","triggers":"Array","validate":"Array","autostart":"Bo}
        "list":{"name":"String","verbose":"Boolean"}
        "delete":{"name":"String","instance":"String"}
        "signal":{"name":"String","instance":"String","signal":"Integer"}
        "update_start":{"name":"String"}
        "update_complete":{"name":"String"}
        "event":{"type":"String","data":"Table"}
        "validate":{"package":"String","type":"String","service":"String"}
        "get_data":{"name":"String","instance":"String","type":"String"}
        "state":{"spawn":"Boolean","name":"String"}
        "watchdog":{"mode":"Integer","timeout":"Integer","name":"String","instance":"String"}
'session' @96b0fd45
        "create":{"timeout":"Integer"}
        "list":{"ubus_rpc_session":"String"}
        "grant":{"ubus_rpc_session":"String","scope":"String","objects":"Array"}
        "revoke":{"ubus_rpc_session":"String","scope":"String","objects":"Array"}
        "access":{"ubus_rpc_session":"String","scope":"String","object":"String","function":"String"}
        "set":{"ubus_rpc_session":"String","values":"Table"}
        "get":{"ubus_rpc_session":"String","keys":"Array"}
        "unset":{"ubus_rpc_session":"String","keys":"Array"}
        "destroy":{"ubus_rpc_session":"String"}
        "login":{"username":"String","password":"String","timeout":"Integer"}
'system' @7962cd42
        "board":{}
        "info":{}
        "reboot":{}
        "watchdog":{"frequency":"Integer","timeout":"Integer","magicclose":"Boolean","stop":"Boolean"}
        "signal":{"pid":"Integer","signum":"Integer"}
        "validate_firmware_image":{"path":"String"}
        "sysupgrade":{"path":"String","force":"Boolean","backup":"String","prefix":"String","command":"String","options":"}
'uci' @8b03b234
        "configs":{}
        "get":{"config":"String","section":"String","option":"String","type":"String","match":"Table","ubus_rpc_session":"}
        "state":{"config":"String","section":"String","option":"String","type":"String","match":"Table","ubus_rpc_session"}
        "add":{"config":"String","type":"String","name":"String","values":"Table","ubus_rpc_session":"String"}
        "set":{"config":"String","section":"String","type":"String","match":"Table","values":"Table","ubus_rpc_session":"S}
        "delete":{"config":"String","section":"String","type":"String","match":"Table","option":"String","options":"Array"}
        "rename":{"config":"String","section":"String","option":"String","name":"String","ubus_rpc_session":"String"}
        "order":{"config":"String","sections":"Array","ubus_rpc_session":"String"}
        "changes":{"config":"String","ubus_rpc_session":"String"}
        "revert":{"config":"String","ubus_rpc_session":"String"}
        "commit":{"config":"String","ubus_rpc_session":"String"}
        "apply":{"rollback":"Boolean","timeout":"Integer","ubus_rpc_session":"String"}
        "confirm":{"ubus_rpc_session":"String"}
        "rollback":{"ubus_rpc_session":"String"}
        "reload_config":{}
'wpa_supplicant' @3632b31c
        "config_add":{"driver":"String","iface":"String","bridge":"String","hostapd_ctrl":"String","ctrl":"String","config}
        "config_remove":{"iface":"String"}

  每个对象所提供的RPC接口名称以及接口的参数类型都可以通过ubus得到。

2.3 netifd interface RPC

在2021-07-26-440eb064版本的netifd源码的ubus.c文件中为每个接口对象注册了一组相同的方法(或者说函数,不过习惯上称之为方法),如下所示:

static struct ubus_method iface_object_methods[] = {
    { .name = "up", .handler = netifd_handle_up },
    { .name = "down", .handler = netifd_handle_down },
    { .name = "renew", .handler = netifd_handle_renew },
    { .name = "status", .handler = netifd_handle_status },
    { .name = "prepare", .handler = netifd_handle_iface_prepare },
    { .name = "dump", .handler = netifd_handle_dump },
    UBUS_METHOD("add_device", netifd_iface_handle_device, dev_link_policy ),
    UBUS_METHOD("remove_device", netifd_iface_handle_device, dev_link_policy ),
    { .name = "notify_proto", .handler = netifd_iface_notify_proto },
    { .name = "remove", .handler = netifd_iface_remove },
    { .name = "set_data", .handler = netifd_handle_set_data },
};

可以发现,netifd中还有一个叫protocol handler的概念。也就是对不同的interface protocol,可以提供不同的handler,来响应各种可能的事件。static类型的protocol被内置在netifd中,而dhcp和pppoe等类型的协议,则以Shell Script的形式提供。

2.4 netifd protocol handler插件

  netifd的protocol handler插件位于/lib/netifd/proto/目录下,名称统一为*.sh。



本文章由作者:佐须之男 整理编辑,原文地址: 浅谈OpenWRT的netifd和ubus
本站的文章和资源来自互联网或者站长的原创,按照 CC BY -NC -SA 3.0 CN协议发布和共享,转载或引用本站文章应遵循相同协议。如果有侵犯版权的资 源请尽快联系站长,我们会在24h内删除有争议的资源。欢迎大家多多交流,期待共同学习进步。

相关推荐