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。









