netifd工具提供如下3个脚本用于网络配置

  • utils.sh脚本
  • netifd-proto.sh脚本
  • netifd-wireless.sh脚本

utils.sh脚本

utils.shnetifd-proto.shnetifd-wireless.sh提供一些基础功能。一般用户不会直接调用。

  • append
  • add_default_handler
  • set_default
  • config_add_int
  • config_add_array
  • config_add_string
  • config_add_boolean

下面仅列举出部分函数定义,详见源码

# /lib/netifd/utils.sh

N="
"

append() {
	local var="$1"
	local value="$2"
	local sep="${3:- }"

	eval "export -- \"$var=\${$var:+\${$var}\${value:+\$sep}}\$value\""
}

add_default_handler() {
	case "$(type $1 2>/dev/null)" in
		*function*) return;;
		*) eval "$1() { return; }"
	esac
}

set_default() {
	local __s_var="$1"
	local __s_val="$2"
	eval "export -- \"$__s_var=\${$__s_var:-\$__s_val}\""
}

_config_add_generic() {
	local type="$1"; shift

	for name in "$@"; do
		json_add_array ""
		json_add_string "" "$name"
		json_add_int "" "$type"
		json_close_array
	done
}

config_add_int() {
	_config_add_generic 5 "$@"
}

config_add_array() {
	_config_add_generic 1 "$@"
}

config_add_string() {
	_config_add_generic 3 "$@"
}

config_add_boolean() {
	_config_add_generic 7 "$@"
}

1.append()函数说明

append() {
	local var="$1"
	local value="$2"
	local sep="${3:- }"

	eval "export -- \"$var=\${$var:+\${$var}\${value:+\$sep}}\$value\""
}

append()函数作用等效于:

  • var为空
var = $value
  • var不为空
    var 的值是新增的value和旧的值的叠加

用法示例:

append PROTO_DNS "$address"
# PROTO_DNS是某个全局变量名
# $address 值

eval第一遍扫描结果如下:

export -- "PROTO_DNS=${PROTO_DNS:+${PROTO_DNS}${value:+$sep}}$value"

其执行情况为:

  • PROTO_DNS为空,则不执行替换,PROTO_DNS=$value
  • PROTO_DNS不为空,需要执行替换,上述表达式变为PROTO_DNS=${PROTO_DNS}${value:+$sep}$value,接下来需要继续判断$value
    • $value为空,原表达式为PROTO_DNS=${PROTO_DNS}$value
    • $value不为为空,原表达式为PROTO_DNS=${PROTO_DNS}$sep$value

2.config_add_int() config_add_string() config_add_array()等函数使用示例

/ # cat test.sh
#!/bin/sh

. /usr/share/libubox/jshn.sh

_config_add_generic() {
        local type="$1"; shift

        echo "$@"
        for name in "$@"; do
                json_add_array ""
                json_add_string "" "$name"
                json_add_int "" "$type"
                json_close_array
        done
}

config_add_int() {
        _config_add_generic 5 "$@"
}

config_add_string() {
        _config_add_generic 3 "$@"
}

json_init

json_add_array "config"

config_add_int mtu
config_add_int auth
config_add_string apn

json_dump

执行结果如下:

{ "config": [ [ "mtu", 5 ], [ "auth", 5 ], [ "apn", 3 ] ] }

config_add_int()、config_add_string()的作用就是创建json数组保存用于后面的参数,一个参数对应一个数组,如果存在多个参数就会生成多个数组。上述示例中,config_add_int()和config_add_string()创建的数组是嵌套进config数组的。

其中的数字表示不同的数据类型:
5 ==> int
3 ==> string
7 ==> bool
1 ==> array

config_add_int()、config_add_string()创建的数组必须嵌套进另一个json数组,不可以单独存在,因为_config_add_generic()函数中json_add_array创建的数组的name为空,后面使用json_add_string添加成员后,只会将这些成员保存至环境变量,使用json_dump打印该json也是{ }空值。

netifd-proto.sh脚本

netifd-proto.sh提供一些配置网络参数的函数,这些函数是基于utils.sh里面的函数实现的。用户可以直接调用netifd-proto.sh里面的部分函数。

  • proto_config_add_int
  • proto_config_add_string
  • proto_config_add_boolean
  • proto_config_add_array
  • proto_init_update
  • proto_set_keep
  • proto_add_dns_server
  • proto_add_ipv4_address
  • proto_add_ipv6_address
  • proto_add_ipv4_route
  • proto_add_ipv6_route
  • proto_send_update
  • proto_run_command
  • proto_kill_command
  • proto_notify_error
  • proto_block_restart
  • proto_set_available
  • proto_setup_failed
  • init_proto

1.proto_init_update()

proto_init_update() {
	local ifname="$1"
	local up="$2"
	local external="$3"

	PROTO_KEEP=0
	PROTO_INIT=1
	PROTO_TUNNEL_OPEN=
	PROTO_IPADDR=
	PROTO_IP6ADDR=
	PROTO_ROUTE=
	PROTO_ROUTE6=
	PROTO_PREFIX6=
	PROTO_DNS=
	PROTO_DNS_SEARCH=
	PROTO_NEIGHBOR=
	PROTO_NEIGHBOR6=
	json_init
	json_add_int action 0
	[ -n "$ifname" -a "*" != "$ifname" ] && json_add_string "ifname" "$ifname"
	json_add_boolean "link-up" "$up"
	[ -n "$3" ] && json_add_boolean "address-external" "$external"
}

初始化一组interface用到的参数,例如ifname,link状态up,ipv4信息PROTO_IPADDR等等。

action对应netifd的一种操作,详见netifd的proto_shell_notify()函数。

同时初始化一个json对象用于保存这些参数,最终这些参数会通过json对象传递到netifd。

2.proto_set_keep()、proto_add_dns_server()、 proto_add_dns_search()、proto_add_ipv4_route()、proto_add_ipv6_route()

以上函数的作用是设置proto_init_update()初始化的那些参数。用户会直接调用这些函数进行参数配置。

这里以proto_add_ipv4_address()函数为例介绍其执行流程:

proto_add_ipv4_address() {
	local address="$1"
	local mask="$2"
	local broadcast="$3"
	local ptp="$4"

	append PROTO_IPADDR "$address/$mask/$broadcast/$ptp"
}

proto_add_ipv4_address "192.168.1.2" "255.255.255.0" "192.168.1.0" "0" 

proto_add_ipv4_address()最多会接收4个参数,经过这一步设置后

PROTO_IPADDR=192.168.1.2/255.255.255.0/192.168.1.0/0

proto_add_ipv4_address()做的事情实际上就这么多,只是设置PROTO_IPADDR的值而已。为了方便理解,接下来会继续讲下PROTO_IPADDR解析和传递过程。

协议脚本最终在发送时会调用proto_send_update()函数向netifd传递这些参数用于更新interface,在发送时先会对参数进行解析,接下来以PROTO_IPADDR参数为例讲解如何解析和传递的。

proto_send_update() {

    ......
	_proto_push_array "ipaddr" "$PROTO_IPADDR" _proto_push_ipv4_addr
	......
}

_proto_push_array()函数执行流程如下:

  • 创建一个名为$1(ipaddr)的json数组
  • 依次用$3处理$2中每一个参数
_proto_push_array() {
	local name="$1"
	local val="$2"
	local cb="$3"

	[ -n "$val" ] || return 0
	json_add_array "$name"
	for item in $val; do
		eval "$cb \"\$item\""
	done
	json_close_array
}

上述$3是 _proto_push_ipv4_addr(),$2是PROTO_IPADDR

PROTO_IPADDR=192.168.1.2/255.255.255.0/192.168.1.0/0

_proto_push_ipv4_addr()的行为如下:

  • PROTO_IPADDR存入变量str
  • address = str删掉第一个/及其右边的字符串 > 192.168.1.1
  • str = str删掉第一个/及其左边的字符串 > 255.255.255.0/192.168.1.0/0
  • mask = str删掉第一个/及其右边的字符串 > 255.255.255.0
  • 以此类推,从PROTO_IPADDR解析出全部4个变量
  • 新建匿名json对象
  • 将上述4个变量添加进json对象
_proto_push_ipv4_addr() {
	local str="$1"
	local address mask broadcast ptp

	address="${str%%/*}"
	str="${str#*/}"
	mask="${str%%/*}"
	str="${str#*/}"
	broadcast="${str%%/*}"
	str="${str#*/}"
	ptp="$str"

	json_add_object ""
	json_add_string ipaddr "$address"
	[ -n "$mask" ] && json_add_string mask "$mask"
	[ -n "$broadcast" ] && json_add_string broadcast "$broadcast"
	[ -n "$ptp" ] && json_add_string ptp "$ptp"
	json_close_object
}

示例code:

#!/bin/sh         
. /usr/share/libubox/jshn.sh                                  
append() {       
        local var="$1"        
        local value="$2"    
        local sep="${3:- }"       
        eval "export -- \"$var=\${$var:+\${$var}\${value:+\$sep}}\$value\""
}     

_proto_push_ipv4_addr() {                                                  
        local str="$1"
        local address mask broadcast ptp       
        address="${str%%/*}"
        str="${str#*/}"
        mask="${str%%/*}"
        str="${str#*/}"
        broadcast="${str%%/*}"
        str="${str#*/}"       
        ptp="$str"       
        json_add_object ""   
        json_add_string ipaddr "$address"
        [ -n "$mask" ] && json_add_string mask "$mask"          
        [ -n "$broadcast" ] && json_add_string broadcast "$broadcast"      
        [ -n "$ptp" ] && json_add_string ptp "$ptp"          
        json_close_object                                                  
}
_proto_push_array() {        
        local name="$1"           
        local val="$2"                                
        local cb="$3"    
        [ -n "$val" ] || return 0       
        json_add_array "$name"
        for item in $val; do
                eval "$cb \"\$item\""    
        done      
        json_close_array         
}    

proto_add_ipv4_address() { 
        local address="$1"
        local mask="$2"
        local broadcast="$3"
        local ptp="$4"
        append PROTO_IPADDR "$address/$mask/$broadcast/$ptp"    
}                                                                       

proto_add_ipv4_address "192.168.1.2" "255.255.255.0" "192.168.1.255" "0"   
echo PROTO_IPADDR = ${PROTO_IPADDR}                             
json_init                                                       
_proto_push_array "ipaddr" "$PROTO_IPADDR" _proto_push_ipv4_addr 
json_dump                                                       

运行结果:

PROTO_IPADDR = 192.168.1.2/255.255.255.0/192.168.1.255/0
{ "ipaddr": [ { "ipaddr": "192.168.1.2", "mask": "255.255.255.0", "broadcast": "192.168.1.255", "ptp": "0" } ] }

3.proto_send_update()

_proto_notify() {
	local interface="$1"
	local options="$2"
	json_add_string "interface" "$interface"
	ubus $options call network.interface notify_proto "$(json_dump)"
}

proto_send_update() {
	local interface="$1"

	proto_close_nested
	json_add_boolean keep "$PROTO_KEEP"
	_proto_push_array "ipaddr" "$PROTO_IPADDR" _proto_push_ipv4_addr
	_proto_push_array "ip6addr" "$PROTO_IP6ADDR" _proto_push_ipv6_addr
	_proto_push_array "routes" "$PROTO_ROUTE" _proto_push_route
	_proto_push_array "routes6" "$PROTO_ROUTE6" _proto_push_route
	_proto_push_array "ip6prefix" "$PROTO_PREFIX6" _proto_push_string
	_proto_push_array "dns" "$PROTO_DNS" _proto_push_string
	_proto_push_array "dns_search" "$PROTO_DNS_SEARCH" _proto_push_string
	_proto_push_array "neighbor" "$PROTO_NEIGHBOR" _proto_push_ipv4_neighbor
	_proto_push_array "neighbor6" "$PROTO_NEIGHBOR6" _proto_push_ipv6_neighbor
	_proto_notify "$interface"
}

proto_send_update()函数的目的是将proto_init_update()中创建的参数:ipaddr,ip6addr,routes等传递到netifd。传递的方式是ubus call network.interface notify_proto

4.proto_run_command()、proto_kill_command()

proto_run_command() {
	local interface="$1"; shift

	json_init
	json_add_int action 1
	json_add_array command
	while [ $# -gt 0 ]; do
		json_add_string "" "$1"
		shift
	done
	json_close_array
	[ -n "$_EXPORT_VARS" ] && {
		json_add_array env
		for var in $_EXPORT_VARS; do
			eval "json_add_string \"\" \"\${$var}\""
		done
		json_close_array
	}
	_proto_notify "$interface"
}

proto_kill_command() {
	local interface="$1"; shift

	json_init
	json_add_int action 2
	[ -n "$1" ] && json_add_int signal "$1"
	_proto_notify "$interface"
}

proto_run_command()是通知netifd运行某个命令;
proto_kill_command()是通知netifd停止运行某个命令;

5.proto_block_restart()

proto_block_restart() {
	local interface="$1"; shift

	json_init
	json_add_int action 4
	_proto_notify "$interface"
}

proto_block_restart()设置struct interface成员autostart = false ,禁止interface自动set up。

6.proto_set_available()

proto_set_available() {
	local interface="$1"
	local state="$2"
	json_init
	json_add_int action 5
	json_add_boolean available "$state"
	_proto_notify "$interface"
}

proto_set_available()设置interface的状态(struct interface成员available),如果状态为true,表明这个interface已经准备好了,可以进行set up。

7.proto_notify_error()

proto_notify_error() {
	local interface="$1"; shift

	json_init
	json_add_int action 3
	json_add_array error
	while [ $# -gt 0 ]; do
		json_add_string "" "$1"
		shift
	done
	json_close_array
	_proto_notify "$interface"
}

proto_notify_error()作用是向netifd发送指定interface的错误码。错误码可以任意的字符串。

8.init_proto()

init_proto() {
	proto="$1"; shift
	cmd="$1"; shift

	case "$cmd" in
		dump)
			add_protocol() {
				no_device=0
				no_proto_task=0
				available=0
				renew_handler=0
				teardown_on_l3_link_down=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_add_boolean teardown-on-l3-link-down "$teardown_on_l3_link_down"
				json_dump
			}
		;;
		setup|teardown|renew)
			interface="$1"; shift
			data="$1"; shift
			ifname="$1"; shift

			add_protocol() {
				[[ "$proto" == "$1" ]] || return 0

				case "$cmd" in
					setup) _proto_do_setup "$1";;
					teardown) _proto_do_teardown "$1" ;;
					renew) _proto_do_renew "$1" ;;
					*) return 1 ;;
				esac
			}
		;;
	esac
}

/lib/netifd/proto/目录下每一种协议执行时都会调用init_proto().然后再通过init_proto()处理dump、setup、teardown等事件。

下面以dhcp.sh协议为例介绍其执行流程:

1.ducp.sh '' dump流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GmgG5y4D-1652324663394)(B58F5119F738417E85B1A2FA495C477B)]

  • init_proto "$@"只会增加一个函数add_protocol()
  • add_protocol dhcp才是真正调用add_protocol()的地方

2.dhcp.sh setup流程

在这里插入图片描述

ducp.sh setup并不是用户手动调用的,通常都是由netifd调用,另外set up一般是需要携带多个参数的。这里只是为了介绍流程而提供的一个简单示例。

netifd-wireless.sh

待续

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐