Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
docs:guide-developer:network-scripting [2021/02/10 03:13] – update vgaeteradocs:guide-developer:network-scripting [2023/07/19 09:34] (current) – [Get WAN gateway for unmanaged default route] add vgaetera
Line 1: Line 1:
 ====== Network scripts ====== ====== Network scripts ======
-netifd can (probably) bring up a wired, static ip configuration without shell scripts. For everything else (PPPoE or 3G) it needs //protocol handlers// implemented as sets of shell functions.+netifd can (probably) bring up a wired, static ip configuration without shell scripts. 
 +For everything else (PPPoE or 3G) it needs //protocol handlers// implemented as sets of shell functions.
  
 ===== Writing protocol handlers ===== ===== Writing protocol handlers =====
-Each protocol handler is a shell script in ''/lib/netifd/proto/''. It is run (or maybe sourced) when netifd daemon starts. Changes made to the scripts do not take effect until netifd restarts. The name of the file usually matches ''option proto'' in ''/etc/config/network''.+Each protocol handler is a shell script in ''/lib/netifd/proto/''. 
 +It is run (or maybe sourced) when netifd daemon starts. 
 +Changes made to the scripts do not take effect until netifd restarts. 
 +The name of the file usually matches ''option proto'' in ''/etc/config/network''.
  
 To be able to access the network functions, the script needs to include the necessary shell scripts at the beginning: To be able to access the network functions, the script needs to include the necessary shell scripts at the beginning:
Line 24: Line 28:
  
 ==== init_config ==== ==== init_config ====
-The main purpose of this function is to let netifd know which parameters does this protocol have. These parameters then can be stored in ''/etc/config/network''.+The main purpose of this function is to let netifd know which parameters does this protocol have. 
 +These parameters then can be stored in ''/etc/config/network''.
  
 <code bash> <code bash>
 proto_protocolname_init_config() { proto_protocolname_init_config() {
-    proto_config_add_string "stringparam" + proto_config_add_string "stringparam" 
-    proto_config_add_int "intparam" + proto_config_add_int "intparam" 
-    proto_config_add_boolean "boolparam" + proto_config_add_boolean "boolparam" 
-    ...+ ...
 } }
 </code> </code>
Line 45: Line 50:
 <code bash> <code bash>
 proto_protocolname_setup() { proto_protocolname_setup() {
-    local config="$1" + local config="$1" 
-    # set up the interface+ # set up the interface
 } }
 </code> </code>
Line 52: Line 57:
 This function must be implemented by any protocol backend. This function must be implemented by any protocol backend.
  
-There's usually no need to call ''ifconfig //iface// up'' at the end of this function. If ''no_device=0'', netifd won't even try to start our profile until the device is already up. It waits for ''operstate=up'' and ''carrier=1'', then starts the profile.+There's usually no need to call ''ifconfig //iface// up'' at the end of this function. 
 +If ''no_device=0'', netifd won't even try to start our profile until the device is already up. 
 +It waits for ''operstate=up'' and ''carrier=1'', then starts the profile.
  
 If ''no_device=1'', netifd will bring it up, when it receives a notification from us: If ''no_device=1'', netifd will bring it up, when it receives a notification from us:
Line 65: Line 72:
 </code> </code>
    
-However, this only works once. If someone called ''ifconfig //iface// down'', netifd won't try to bring it up again (at least in BB), so just in case you may put the "up" command in the function.+However, this only works once. 
 +If someone called ''ifconfig //iface// down'', netifd won't try to bring it up again (at least in BB), so just in case you may put the "up" command in the function.
  
 ==== Teardown ==== ==== Teardown ====
-Protocols that need special shutdown handling, for example to kill associated daemons, may implement +Protocols that need special shutdown handling, for example to kill associated daemons, may implement a stop procedure. 
-a stop procedure. This procedure is called when an interface is brought down before the associated UCI +This procedure is called when an interface is brought down before the associated UCI state is cleared.
-state is cleared.+
  
 The function is called when we do ''ifdown //profile//'' or when ''no_device=0'' and netifd detects link connectivity loss. The function is called when we do ''ifdown //profile//'' or when ''no_device=0'' and netifd detects link connectivity loss.
Line 80: Line 87:
 <code bash> <code bash>
 proto_protocolname_teardown() { proto_protocolname_teardown() {
-    local config="$1" + local config="$1" 
-    local iface="$2" + local iface="$2" 
-    # tear down the interface+ # tear down the interface
 } }
 </code> </code>
Line 94: Line 101:
 <code bash> <code bash>
 proto_protocolname_init_config() { proto_protocolname_init_config() {
-    no_device=1 + no_device=1 
-    available=1 + available=1 
-    ...+ ...
 } }
 </code> </code>
Line 105: Line 112:
   * There is a pending patch: https://patchwork.ozlabs.org/project/openwrt/patch/20201229233319.164640-1-me@irrelefant.net/.   * There is a pending patch: https://patchwork.ozlabs.org/project/openwrt/patch/20201229233319.164640-1-me@irrelefant.net/.
  
-==== Available Proto Flags ==== +==== Available proto flags ==== 
-Flags can be added to a proto handler in ''proto_protoname_init_config'', by setting their value to ''1''. The information about all loaded protocols can be obtained by calling //ubus call network get_proto_handlers//.+Flags can be added to a proto handler in ''proto_protoname_init_config'', by setting their value to ''1''. 
 +The information about all loaded protocols can be obtained by calling //ubus call network get_proto_handlers//.
  
 ^ Name ^ Name in //ubus call network get_proto_handlers// ^ Meaning ^ ^ Name ^ Name in //ubus call network get_proto_handlers// ^ Meaning ^
 | no_device | no_device | The interface does not have a lower device. <color #ff7f27>TBD: Explain implications.</color>| | no_device | no_device | The interface does not have a lower device. <color #ff7f27>TBD: Explain implications.</color>|
- | immediate | <color #ff7f27>TBD: I only saw this for the proto static. Maybe this is impossible to set from shell protos?</color> |+| | immediate | <color #ff7f27>TBD: I only saw this for the proto static. Maybe this is impossible to set from shell protos?</color> |
 | available | init_available | Initialize the device directly as available, without the device specified by ifname being present <color #ff7f27>TBD: Is that correct?</color>| | available | init_available | Initialize the device directly as available, without the device specified by ifname being present <color #ff7f27>TBD: Is that correct?</color>|
 | renew_handler | renew_available | Has a renew handler, which can be called. <color #ff7f27>TBD: How can it be called? Is it called automatically by sth.?</color> | | renew_handler | renew_available | Has a renew handler, which can be called. <color #ff7f27>TBD: How can it be called? Is it called automatically by sth.?</color> |
Line 117: Line 125:
 | no_proto_task | no_task | <color #ff7f27>TBD</color> | | no_proto_task | no_task | <color #ff7f27>TBD</color> |
  
-==== Error Codes ==== +==== Error codes ==== 
-If errors for interfaces occur, the json object in //ifstatus interfacename// or in //ubus call network.interface dump// have an attribute //"error"//. If there are no errors, this attribute is not existing.+If errors for interfaces occur, the json object in //ifstatus interfacename// or in //ubus call network.interface dump// have an attribute //"error"//. 
 +If there are no errors, this attribute is not existing.
  
 ^ CODE ^ Meaning ^ ^ CODE ^ Meaning ^
Line 125: Line 134:
 | SETUP_FAILED | <color #ff7f27>TBD</color> | | SETUP_FAILED | <color #ff7f27>TBD</color> |
  
-Custom error codes can be thrown from the proto scripts aswell. This is done via ''proto_notify_error "$config" MY_CUSTOM_ERROR_ID''.+Custom error codes can be thrown from the proto scripts aswell. 
 +This is done via ''proto_notify_error "$config" MY_CUSTOM_ERROR_ID''.
  
 ==== How does it work internally? ==== ==== How does it work internally? ====
Line 158: Line 168:
  
 === notification functions === === notification functions ===
-Note: some of these function exit immediately. These functions are dispatched to ubus and arrive here: [[commit>?p=project/netifd.git;a=blob;f=proto-shell.c;hb=c00c8335d6188daa326ecfe5a62da15a9b9987e1#l792]]+Note: some of these function exit immediately. 
 +These functions are dispatched to ubus and arrive [[commit>?p=project/netifd.git;a=blob;f=proto-shell.c;hb=c00c8335d6188daa326ecfe5a62da15a9b9987e1#l792|here]]
  
   * ''proto_block_restart $interface''   * ''proto_block_restart $interface''
Line 166: Line 177:
   * ''proto_run_command''   * ''proto_run_command''
  
-We can start a daemon that maintains the connection asynchronously by calling ''proto_run_command "$config" //custom_script//''. Netifd remembers its pid. It can be killed manually by calling ''proto_kill_command "$config"''. Netifd can automatically kill it, when the profile stopped. +We can start a daemon that maintains the connection asynchronously by calling ''proto_run_command "$config" //custom_script//''. 
 +Netifd remembers its pid. 
 +It can be killed manually by calling ''proto_kill_command "$config"''. 
 +Netifd can automatically kill it, when the profile stopped.
  
  * ''proto_add_host_dependency''  * ''proto_add_host_dependency''
  
-It seems to avoid race conditions in protos. We can register the following type of dependencies by calling:+It seems to avoid race conditions in protos. 
 +We can register the following type of dependencies by calling:
   * ''proto_add_host_dependency "$config" "$host" "$ifname"''   * ''proto_add_host_dependency "$config" "$host" "$ifname"''
   * ''proto_add_host_dependency "$config" \'\' "$ifname"'' (only wait for an iface)   * ''proto_add_host_dependency "$config" \'\' "$ifname"'' (only wait for an iface)
   * (maybe more?)   * (maybe more?)
  
-Only if ''$iface'' is up, the corresponding ''$config'' will be loaded. So we need another proto to be completed, we can use this here. \\+Only if ''$iface'' is up, the corresponding ''$config'' will be loaded. 
 +So we need another proto to be completed, we can use this here.
  
-From some observations I think it looks like technically the ''$config'' is initially loaded, then the ''proto_add_host_dependency'' is called inside the proto handler and then the ''$config'' is removed again until ''$iface'' is available, up, ... or so. However, currently I do not know where to find the source code, so this is only a vague idea of what happens.+From some observations I think it looks like technically the ''$config'' is initially loaded, then the ''proto_add_host_dependency'' is called inside the proto handler and then the ''$config'' is removed again until ''$iface'' is available, up, ... or so. 
 +However, currently I do not know where to find the source code, so this is only a vague idea of what happens.
  
   * ''proto_send_update $interface ''   * ''proto_send_update $interface ''
Line 188: Line 205:
   * '' json_get_var ''   * '' json_get_var ''
   * '' json_get_vars ''   * '' json_get_vars ''
 +
 +===== Examples =====
 +[[https://github.com/openwrt/openwrt/blob/master/package/base-files/files/lib/functions/network.sh|Network functions]] rely on runtime configuration and can return unexpected result if you are using MWAN or VPN.
 +Replace automatic WAN detection with explicit interface definition if necessary.
 +
 +==== Get LAN address ====
 +<code bash>
 +# Runtime configuration
 +NET_IF="lan"
 +. /lib/functions/network.sh
 +network_flush_cache
 +network_get_ipaddr NET_ADDR "${NET_IF}"
 +network_get_ipaddr6 NET_ADDR6 "${NET_IF}"
 +echo "${NET_ADDR}"
 +echo "${NET_ADDR6}"
 +</code>
 +
 +==== Get WAN interface ====
 +<code bash>
 +# Runtime configuration
 +. /lib/functions/network.sh
 +network_flush_cache
 +network_find_wan NET_IF
 +network_find_wan6 NET_IF6
 +echo "${NET_IF}"
 +echo "${NET_IF6}"
 +</code>
 +
 +==== Get WAN L2 device ====
 +<code bash>
 +# Runtime configuration
 +. /lib/functions/network.sh
 +network_flush_cache
 +network_find_wan NET_IF
 +network_find_wan6 NET_IF6
 +network_get_physdev NET_L2D "${NET_IF}"
 +network_get_physdev NET_L2D6 "${NET_IF6}"
 +echo "${NET_L2D}"
 +echo "${NET_L2D6}"
 +</code>
 +
 +==== Get WAN L3 device ====
 +<code bash>
 +# Runtime configuration
 +. /lib/functions/network.sh
 +network_flush_cache
 +network_find_wan NET_IF
 +network_find_wan6 NET_IF6
 +network_get_device NET_L3D "${NET_IF}"
 +network_get_device NET_L3D6 "${NET_IF6}"
 +echo "${NET_L3D}"
 +echo "${NET_L3D6}"
 +
 +# Persistent configuration
 +uci get network.wan.device
 +uci get network.wan6.device
 +</code>
 +
 +==== Get WAN address ====
 +<code bash>
 +# Runtime configuration
 +. /lib/functions/network.sh
 +network_flush_cache
 +network_find_wan NET_IF
 +network_find_wan6 NET_IF6
 +network_get_ipaddr NET_ADDR "${NET_IF}"
 +network_get_ipaddr6 NET_ADDR6 "${NET_IF6}"
 +echo "${NET_ADDR}"
 +echo "${NET_ADDR6}"
 +
 +# Persistent static configuration
 +uci get network.wan.ipaddr
 +uci get network.wan6.ip6addr
 +</code>
 +
 +==== Get WAN gateway ====
 +<code bash>
 +# Runtime configuration
 +. /lib/functions/network.sh
 +network_flush_cache
 +network_find_wan NET_IF
 +network_find_wan6 NET_IF6
 +network_get_gateway NET_GW "${NET_IF}"
 +network_get_gateway6 NET_GW6 "${NET_IF6}"
 +echo "${NET_GW}"
 +echo "${NET_GW6}"
 +
 +# Persistent static configuration
 +uci get network.wan.gateway
 +uci get network.wan6.ip6gw
 +</code>
 +
 +==== Get WAN prefix ====
 +<code bash>
 +# Runtime configuration
 +. /lib/functions/network.sh
 +network_flush_cache
 +network_find_wan6 NET_IF6
 +network_get_prefix6 NET_PFX6 "${NET_IF6}"
 +echo "${NET_PFX6}"
 +
 +# Persistent static configuration
 +uci get network.wan6.ip6prefix
 +</code>
 +
 +==== Get WAN gateway for unmanaged default route ====
 +<code bash>
 +# Runtime configuration
 +ubus call network.interface dump \
 +| jsonfilter -e "$['interface'][*]['inactive']
 +['route'][*]['target'='0.0.0.0','mask'='0','nexthop']"
 +</code>
  
  • Last modified: 2021/02/10 03:13
  • by vgaetera