OpenWrt源码分析之netifd

regist ubus method

首先,netifd初始化时会向ubusd支持一系列的object & method,在netifd_ubus_init中添加object:main_object, dev_object, wireless_object, iface_object;脚本(例如ifup、devstatus等)通过ubus call来执行netifd的method。

main_object

object name为”network”,method列表如下:

namehandler
restartnetifd_handle_restart
reloadnetifd_handle_reload
add_host_routenetifd_add_host_route
get_proto_handlersnetifd_get_proto_handlers
add_dynamicnetifd_add_dynamic
root@OpenWrt:/# ubus -v list network
'network' @90c358a9
        "restart":{}
        "reload":{}
        "add_host_route":{"target":"String","v6":"Boolean","interface":"String"}
        "get_proto_handlers":{}
        "add_dynamic":{"name":"String"}

dev_object

object name为”network.device”,method列表如下:

namehandler
statusnetifd_dev_status
set_aliasnetifd_handle_alias
set_statenetifd_handle_set_state
root@OpenWrt:/# ubus -v list network.device
'network.device' @9fa99d73
        "status":{"name":"String"}
        "set_alias":{"alias":"Array","device":"String"}
        "set_state":{"name":"String","defer":"Boolean"}

wireless_object

object name为”network.wireless”,method列表如下:

namehandler
upnetifd_handle_wdev_up
downnetifd_handle_wdev_down
statusnetifd_handle_wdev_status
notifynetifd_handle_wdev_notify
get_validatenetifd_handle_wdev_get_validate
root@OpenWrt:/# ubus -v list network.wireless
'network.wireless' @4cb96ce9
        "up":{}
        "down":{}
        "status":{}
        "notify":{}
        "get_validate":{}

face_object

object name为”network.interface”,method列表如下:

namehandler
upnetifd_handle_up
downnetifd_handle_down
statusnetifd_handle_status
preparenetifd_handle_iface_prepare
dumpnetifd_handle_dump
add_devicenetifd_iface_handle_device
remove_devicenetifd_iface_handle_device
notify_protonetifd_iface_notify_proto
removenetifd_iface_remove
set_datanetifd_handle_set_data
root@OpenWrt:/# ubus -v list network.interface
'network.interface' @1193bd55
        "up":{}
        "down":{}
        "status":{}
        "prepare":{}
        "dump":{}
        "add_device":{"name":"String","link-ext":"Boolean"}
        "remove_device":{"name":"String","link-ext":"Boolean"}
        "notify_proto":{}
        "remove":{}
        "set_data":{}

add proto

proto是interface获取IP配置的方式,包括static、DHCP、DHCPv6、PPPoE等,其中static(proto_static)是在代码中定义的,而DHCP、PPPoE定义在shell scripts上(proto_shell),scripts位于/lib/netifd/proto/目录下。

static proto

static struct proto_handler static_proto = {
    .name = "static",
    .flags = PROTO_FLAG_IMMEDIATE |
         PROTO_FLAG_FORCE_LINK_DEFAULT,
    .config_params = &proto_ip_attr,
    .attach = static_attach,
};

config_params包括ipaddr、ip6addr、netmask、broadcast、gateway、ip6gw、ip6prefix;
如果一个interface需要使用到该proto,则会使用它的attach方法。

shell proto

proto_shell_init会进到/lib/netifd/proto/目录下解析*.sh,以dhcp.sh为例,popen执行命令”/bin/sh -c dhcp.sh ” dump”,执行结果是一个json格式的字符串:

./dhcp.sh '' dump
{ "name": "dhcp", "config": [ [ "ipaddr:ipaddr", 3 ], [ "hostname:hostname", 3 ]
, [ "clientid", 3 ], [ "vendorid", 3 ], [ "broadcast:bool", 7 ], [ "reqopts:list
(string)", 3 ], [ "iface6rd", 3 ], [ "sendopts", 3 ], [ "delegate", 7 ], [ "zone
6rd", 3 ], [ "zone", 3 ], [ "mtu6rd", 3 ], [ "customroutes", 3 ] ], "no-device":
 false, "no-proto-task": false, "available": false, "renew-handler": true, "last
error": false }

分析一下执行脚本命令”dhcp.sh ” dump”做了哪些事情,进到dhcp.sh,首先执行函数init_proto “$@”,init_proto定义在netifd-proto.sh中,init_proto函数中,cmd=dump,也就是定义了函数add_protocol;

add_protocol() {
        no_device=0
        no_proto_task=0
        available=0
        renew_handler=0

        add_default_handler "proto_$1_init_config"

        json_init
        json_add_string "name" "$1"
        json_add_array "config"
        eval "proto_$1_init_config"
        json_close_array
        json_add_boolean no-device "$no_device"
        json_add_boolean no-proto-task "$no_proto_task"
        json_add_boolean available "$available"
        json_add_boolean renew-handler "$renew_handler"
        json_add_boolean lasterror "$lasterror"
        json_dump
}

回到dhcp.sh,文件的最后会执行函数add_protocol dhcp,实际上就是装置一个json结构的字符串,包括key:name、config、no-device、no-proto-task、available、renew-handler和lasterror;config为array类型,在proto_dhcp_init_config中组装,proto_dhcp_init_config函数定义在dhcp.sh中,向config添加成员ipaddr:ipaddr、hostname:hostname、clientid、vendorid、broadcast:bool、reqopts:list(string)、iface6rd、sendopts、delegate、zone6rd、zone、mtu6rd、customroutes,这些及时DHCP需要的参数。
proto_shell_add_handler中封装一个proto_handle *proto:

proto->name = "dhcp"
proto->attach = proto_shell_attach
proto->flags //依赖于dump中no-device、no-proto-task、available、renew-handler和lasterror
proto->config_params //从dump中的config导入,包括ipaddr、hostname、clientid、vendorid、broadcast、reqopts、iface6rd、sendopts、delegate、zone6rd、zone、mtu6rd、customroutes

最后通过add_proto_handler将proto添加到全局avl tree中,也就是句柄handlers中。

init device & interface

netifd中的device代表物理设备,例如eth0;
如果需要管理device的状态,则注册一个device_user,device_user绑定到对于的device上,而且包含device event事件的回调函数,如果device的device_user计数为0,则device结构体会被自动释放掉;
bridge或vlan是特殊的device,它们可以引用其他device;
device的up/down状态由device_user计数决定,claim_device增加device_user计数,则bring up,release_device减少device_user计数,当计数减为0时,则bring down;

netifd中的interface代表应用在一个或多个device上的配置;
interface必须绑定一个main device和一个l3 device,如果使用static或者dhcp proto时,interface的l3 device指向main device;如果使用pppoe,则l3 device指向ppp0;

config_init_all会调用config_init_devices、config_init_interfaces、config_init_routes、config_init_rules、config_init_globals、config_init_wireless来创建device、interface、wireless等;
其中device、interface、route、rules、globals由/etc/config/network的配置参数来生成,wireless由/etc/config/wireles的配置参数来生成。

/etc/config/network的内容如下:

config interface 'lan'
        option type 'bridge'
        option ifname 'eth0 eth1'
        option proto 'static'
        option ipaddr '192.168.1.1'
        option netmask '255.255.255.0'
        option ip6assign '60'

config interface 'wan'
        option ifname 'eth2'
        option proto 'dhcp'

上面定义了两个section,type都是interface,可见没有创建device,所以只看config_init_interfaces这个函数;

config_parse_interface会先判断有没有option disabled 1,如果有,则直接return,否则创建interface并添加到全局avl tree(interfaces)中,如果有option type ‘bridge’,则使用config_parse_bridge_interface创建devices,再使用interface_alloc创建interface;

上面的config中interface lan是bridge,interface wan为普通interface,下面先看看config_parse_bridge_interface;
interface name是lan,则会创建一个name是br-lan的device,device_type为bridge_device_type;

const struct device_type bridge_device_type = {
    .name = "Bridge",
    .config_params = &bridge_attr_list,

    .create = bridge_create,
    .config_init = bridge_config_init,
    .reload = bridge_reload,
    .free = bridge_free,
    .dump_info = bridge_dump_info,
};

先调用type->create,也就是bridge_create,再调用type->config_init,也就是bridge_config_init;

进到bridge_create,调用链如下所示:

bridge_create
    --> device_init
        --> device_init_virtual

最后设置device的set_state方法是bridge_set_state,将device添加到全局avl tree(devices)中去;

再看bridge_config_init,bridge_add_member根据config中的ifname添加member,ifname包括eth0和eth1,device_get(name, true)会先从avl tree devices中查找,如果没有查找到则创建,初始化时并没有eth0和eth1 device,在device_get会创建这两个device,device_type为simple_device_type;

添加member到bridge_state的vlist_tree members后,会执行vlist的update函数,即bridge_member_update;bridge_member_update中会给之前创建的eth0/eth1 device添加device_user,bridge_member作为bridge device的device_user,在bridge_member结构体中会有一个成员struct device_user dev,bm->dev.cb = bridge_member_cb;bridge_enable_member中device_claim将eth0/eth1 bring up,system_bridge_addif桥接到br-lan上;

interface_alloc申请interface,将interface添加到全局avl list interfaces中,执行avl_list update回调函数,interface_update,interface_update做了三件事:
1. proto_init_interface
interface attach到proto
2. interface_claim_device
interface绑定main device和l3 device;
3. netifd_ubus_add_interface
注册ubus object network.interface.xxx,method:up、down、status、prepare、dump、add_device、remove_device、notify_proto、remove、set_data.

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
OpenWrt是一个开源的嵌入式操作系统,主要用于路由器和其他网络设备。通过分析OpenWrt源码,我们可以了解其编译过程和目录结构。 OpenWrt源码包括多个子目录,其中最重要的是根目录下的Makefile文件。这个Makefile文件是执行make命令时的入口。在Makefile中,可以定义编译选项、目标和依赖关系。 在Makefile中,有一个名为"world"的目标,它是整个编译过程的入口。在这个目标中,根据是否定义了"OPENWRT_BUILD"变量,会执行不同的逻辑。如果"OPENWRT_BUILD"变量未定义,会执行第一个逻辑;如果定义了,会执行第二个逻辑。 整个编译过程涉及到多个子目录的目标生成。每个子目录都有自己的Makefile文件,用于定义该子目录下的编译选项、目标和依赖关系。通过递归调用子目录的Makefile,可以完成整个OpenWrt的编译过程。 在编译过程中,还包括了内核的编译过程、固件的生成过程和软件包的编译过程。这些过程都在相应的子目录中完成。 总结来说,通过分析OpenWrt源码,我们可以了解其编译过程和目录结构。Makefile文件是整个编译过程的入口,通过递归调用子目录的Makefile,完成各个子目录的目标生成。同时,还包括了内核的编译过程、固件的生成过程和软件包的编译过程。 #### 引用[.reference_title] - *1* *2* *3* [openwrt源码框架解析](https://blog.csdn.net/daidi1989/article/details/53336845)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值