Openwrt 修改默认ip脚本分析

/etc/board.json生成

  1. etc/init.d/boot

    boot() {
    	/bin/config_generate
    }
    
  2. /bin/config_generate

    #!/bin/sh
    
    CFG=/etc/board.json
    
    . /usr/share/libubox/jshn.sh
    
    # /etc/board.json文件存在不存在或者为空,则调用 board_detect 生成该文件,否则往下进行
    # 第一次启动肯定不会存在该文件,所以肯定执行 board_detect
    [ -s $CFG ] || /bin/board_detect || exit 1
    [ -s /etc/config/network -a -s /etc/config/system ] && exit 0
    
  3. /bin/board_detect

    #!/bin/sh
    
    CFG=$1
    
    [ -n "$CFG" ] || CFG=/etc/board.json
    
    # 如果/etc/board.d/是个目录,且 /etc/board.json 文件存在不存在或者为空
    # 变量/etc/board.d/下面的脚本,然后执行
    [ -d "/etc/board.d/" -a ! -s "$CFG" ] && {
    	for a in $(ls /etc/board.d/*); do
    		[ -x $a ] || continue;
    		$(. $a)
    	done
    }
    
    [ -s "$CFG" ] || return 1
    
  4. /etc/board.d/目录下脚本如下:

    • 02_network:来源 target/linux/mpc85xx/base-files/etc/board.d/02_network

      #!/bin/sh
      # Copyright (C) 2014-2015 OpenWrt.org
      
      . /lib/functions/uci-defaults.sh
      . /lib/functions.sh
      . /lib/functions/system.sh
      
      board_config_update
      # board=fsl,P2020RDB 来源dts compatible = "fsl,P2020RDB";
      board=$(board_name)
      
      case "$board" in
      fsl,P2020RDB)
      	# 这里把eth0 eth1 都设置为lan口
      	ucidef_set_interface_lan "eth0 eth1"
      	;;
      *)
      	# 这里把eth0设置为lan口,把eth1 设置为wan口
      	ucidef_set_interfaces_lan_wan "eth0" "eth1"
      	;;
      esac
      
      board_config_flush
      
      exit 0
      

      注意:ucidef_set_interface_lan和ucidef_set_interfaces_lan_wan函数会生成/etc/board.json文件。这两个函数存放位置:package/base-files/files/lib/functions/uci-defaults.sh

      • ucidef_set_interface_lan函数

        ucidef_set_interface_lan() {
        	ucidef_set_interface "lan" device "$1" protocol "${2:-static}"
        }
        
      • ucidef_set_interfaces_lan_wan函数

        ucidef_set_interfaces_lan_wan() {
        	local lan_if="$1"
        	local wan_if="$2"
        
        	ucidef_set_interface_lan "$lan_if"
        	ucidef_set_interface_wan "$wan_if"
        }
        
      • ucidef_set_interface函数

        以ucidef_set_interface_lan "eth0 eth1" 为例说明该函数功能:
        
        - ucidef_set_interface "lan" device "eth0 eth1" protocol "${2:-static}"
          - $1 = lan
          - $2 = device 
          - $3 = "eth0 eth1"
          - $4 = protocol 
          - $5 = static
        

        下面分析代码

        ucidef_set_interface() {
        	# network=lan
        	local network=$1; shift
        	# 选中/etc/board.json 里面的 network下的lan节点
        	json_select_object network
        	json_select_object "$network"
        	# 此时$1=device
        	while [ -n "$1" ]; do
        		local opt=$1; shift # opt=device
        		local val=$1; shift # val = "eth0 eth1"
        
        		[ -n "$opt" -a -n "$val" ] || break
        		# 如果 val是列表,会添加json数组
        		# 大概是这个样子 "ports": ["eth0", "eth1"],
        		[ "$opt" = "device" -a "$val" != "${val/ //}" ] && {
        			# 这里处理device 多个网口 桥接到lan
        			json_select_array "ports"
        			for e in $val; do json_add_string "" "$e"; done
        			json_close_array
        		} || {
        			# lan下的"protocol"节点
        			json_add_string "$opt" "$val"
        		}
        	done
        	
        	# 下面如果 没有protocol 设置默认的值,
        	if ! json_is_a protocol string; then
        		case "$network" in
        			lan) json_add_string protocol static ;;
        			wan) json_add_string protocol dhcp ;;
        			*) json_add_string protocol none ;;
        		esac
        	fi
        
        	json_select ..
        	json_select ..
        }
        

        经过上面的代码后,就会生成/etc/board.json

        {
                "model": {
                        "id": "fsl,P2020RDB",
                        "name": "Freescale P2020RDB"
                },
                "network": {
                        "lan": {
                                "ports": [
                                        "eth0",
                                        "eth1"
                                ],
                                "protocol": "static"
                        }
                }
        }
        
    • 99-default_network:来源 package/base-files/files/etc/board.d/99-default_network

      #!/bin/sh
      #
      # Copyright (C) 2013-2015 OpenWrt.org
      #
      
      . /lib/functions/uci-defaults.sh
      
      board_config_update
      # 判断/etc/board.json里面是否有 network 节点,如果有直接退出,没有的话往下
      json_is_a network object && exit 0
      
      # 理论上不会到这,因为在 02_network 中肯定生成了 /etc/board.json中的network节点
      # 能跑到这说明没有network 节点,这里会设置节点,
      #
      ucidef_set_interface_lan 'eth0'
      [ -d /sys/class/net/eth1 ] && ucidef_set_interface_wan 'eth1'
      
      board_config_flush
      
      exit 0
      

config_generate继续分析

在上个章节第2步骤,通过[ -s $CFG ] || /bin/board_detect || exit 1 会生成默认的/etc/board.json文件,接下来会继续根据给json文件生成/etc/config/network,下面继续分析。

/bin/config_generate文件下面的内容:

# 加载/etc/board.json文件到shell
json_init
json_load "$(cat ${CFG})"

umask 077

# 如果network不存在或者为空
if [ ! -s /etc/config/network ]; then
	bridge_name=""
	# 创建该文件
	touch /etc/config/network
	# 生成config interface 'loopback'和config globals 'globals'节点
	generate_static_network

	# 获取json中的bridge 这里是空的,json里面没有bridge
	json_get_vars bridge
	[ -n "$bridge" ] && {
		# 由于是空的,所以不会到这里,但是如果不是空的
		json_select bridge
		json_get_vars name macaddr
		generate_bridge "$name" "$macaddr"
		json_select ..
		bridge_name=$name
	}
	# 按照上面提到的json文件,这里keys=lan
	# 所以执行的是 bridge_name=""
	# generate_network lan $bridge_name
	json_get_keys keys network
	for key in $keys; do generate_network $key $bridge_name; done

	json_get_keys keys switch
	for key in $keys; do generate_switch $key; done
fi

generate_network代码如下:

addr_offset=2
generate_network() {
	local ports device macaddr protocol type ipaddr netmask vlan
	local bridge=$2

	json_select network
		json_select "$1"
			json_get_vars device macaddr metric protocol ipaddr netmask vlan
			json_get_values ports ports
		json_select ..
	json_select ..

	[ -n "$device" -o -n "$ports" ] || return

	# Force bridge for "lan" as it may have other devices (e.g. wireless)
	# bridged
	[ "$1" = "lan" -a -z "$ports" ] && {
		ports="$device"
	}

	[ -n "$ports" -a -z "$bridge" ] && {
		uci -q batch <<-EOF
			add network device
			set network.@device[-1].name='br-$1'
			set network.@device[-1].type='bridge'
		EOF
		for port in $ports; do uci add_list network.@device[-1].ports="$port"; done
		[ -n "$macaddr" ] && {
			for port in $ports; do
				uci -q batch <<-EOF
					add network device
					set network.@device[-1].name='$port'
					set network.@device[-1].macaddr='$macaddr'
				EOF
			done
		}
		device=br-$1
		type=
		macaddr=""
	}

	[ -n "$bridge" ] && {
		[ -z "$ports" ] && ports="$device"
		if [ -z "$vlan" ]; then
			bridge_vlan_id=$((bridge_vlan_id + 1))
			vlan=$bridge_vlan_id
		fi
		generate_bridge_vlan $1 $bridge "$ports" $vlan
		device=$bridge.$vlan
		type=""
	}

	if [ -n "$macaddr" ]; then
		uci -q batch <<-EOF
			add network device
			set network.@device[-1].name='$device'
			set network.@device[-1].macaddr='$macaddr'
		EOF
	fi

	uci -q batch <<-EOF
		delete network.$1
		set network.$1='interface'
		set network.$1.type='$type'
		set network.$1.device='$device'
		set network.$1.metric='$metric'
		set network.$1.proto='none'
	EOF

	case "$protocol" in
		static)
			local ipad
			case "$1" in
				lan) ipad=${ipaddr:-"192.168.10.1"} ;;
				*) ipad=${ipaddr:-"192.168.$((addr_offset++)).1"} ;;
			esac

			netm=${netmask:-"255.255.255.0"}

			uci -q batch <<-EOF
				set network.$1.proto='static'
				set network.$1.ipaddr='$ipad'
				set network.$1.netmask='$netm'
			EOF
			[ -e /proc/sys/net/ipv6 ] && uci set network.$1.ip6assign='60'
		;;

		dhcp)
			# fixup IPv6 slave interface if parent is a bridge
			[ "$type" = "bridge" ] && device="br-$1"

			uci set network.$1.proto='dhcp'
			[ -e /proc/sys/net/ipv6 ] && {
				uci -q batch <<-EOF
					delete network.${1}6
					set network.${1}6='interface'
					set network.${1}6.device='$device'
					set network.${1}6.proto='dhcpv6'
				EOF
			}
		;;

		pppoe)
			uci -q batch <<-EOF
				set network.$1.proto='pppoe'
				set network.$1.username='username'
				set network.$1.password='password'
			EOF
			[ -e /proc/sys/net/ipv6 ] && {
				uci -q batch <<-EOF
					set network.$1.ipv6='1'
					delete network.${1}6
					set network.${1}6='interface'
					set network.${1}6.device='@${1}'
					set network.${1}6.proto='dhcpv6'
				EOF
			}
		;;
	esac
}