🗽 Sync 2026-09-18 06:32:12

This commit is contained in:
github-actions[bot] committed 2026-09-18 06:32:12 +08:00
1 parent bee41de29d
commit 6c353312b5
18 files changed
+1226

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=netifd
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=$(PROJECT_GIT)/project/netifd.git
PKG_SOURCE_DATE:=2026-02-26
PKG_SOURCE_VERSION:=cbb83a1857407a28a63dc09412a1f209195914ef
PKG_MIRROR_HASH:=skip
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
PKG_LICENSE:=GPL-2.0
PKG_LICENSE_FILES:=
PKG_BUILD_FLAGS:=lto
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/cmake.mk
define Package/netifd
SECTION:=base
CATEGORY:=Base system
DEPENDS:=+libuci +libnl-tiny +libubus +ubus +ubusd +jshn +libubox +libudebug \
+ucode +ucode-mod-fs +ucode-mod-ubus +ucode-mod-uloop +ucode-mod-uci
TITLE:=OpenWrt Network Interface Configuration Daemon
endef
define Package/netifd/conffiles
/etc/udhcpc.user
/etc/udhcpc.user.d/
endef
TARGET_CFLAGS += \
-I$(STAGING_DIR)/usr/include/libnl-tiny \
-I$(STAGING_DIR)/usr/include
CMAKE_OPTIONS += \
-DLIBNL_LIBS=-lnl-tiny \
-DDEBUG=1
define Package/netifd/install
$(INSTALL_DIR) $(1)/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/netifd $(1)/sbin/
$(CP) ./files/* $(1)/
$(INSTALL_DIR) $(1)/etc/udhcpc.user.d/
$(CP) \
$(PKG_BUILD_DIR)/scripts/utils.sh \
$(PKG_BUILD_DIR)/scripts/netifd-proto.sh \
$(1)/lib/netifd/
endef
$(eval $(call BuildPackage,netifd))
@@ -0,0 +1,6 @@
[ ifup = "$ACTION" ] && {
uci_toggle_state network "$INTERFACE" up 1
[ -n "$DEVICE" ] && {
uci_toggle_state network "$INTERFACE" ifname "$DEVICE"
}
}
+143
View File
@@ -0,0 +1,143 @@
#!/bin/sh /etc/rc.common
START=20
STOP=90
USE_PROCD=1
init_switch() {
setup_switch() { return 0; }
include /lib/network
setup_switch
}
start_service() {
init_switch
procd_open_instance
procd_set_param command /sbin/netifd
procd_set_param respawn
procd_set_param watch network.interface
[ -e /proc/sys/kernel/core_pattern ] && {
procd_set_param limits core="unlimited"
}
procd_close_instance
}
reload_service() {
local rv=0
init_switch
ubus call network reload || rv=1
return $rv
}
stop_service() {
[ -x /sbin/wifi ] && /sbin/wifi down
ifdown -a
sleep 1
}
validate_atm_bridge_section()
{
uci_validate_section network "atm-bridge" "${1}" \
'unit:uinteger:0' \
'vci:range(32, 65535):35' \
'vpi:range(0, 255):8' \
'atmdev:uinteger:0' \
'encaps:or("llc", "vc"):llc' \
'payload:or("bridged", "routed"):bridged'
}
validate_route_section()
{
uci_validate_section network route "${1}" \
'interface:string' \
'target:cidr4' \
'netmask:netmask4' \
'gateway:ip4addr' \
'metric:uinteger' \
'mtu:uinteger' \
'table:or(range(0,65535),string)'
}
validate_route6_section()
{
uci_validate_section network route6 "${1}" \
'interface:string' \
'target:cidr6' \
'gateway:ip6addr' \
'metric:uinteger' \
'mtu:uinteger' \
'table:or(range(0,65535),string)'
}
validate_rule_section()
{
uci_validate_section network rule "${1}" \
'in:string' \
'out:string' \
'src:cidr4' \
'dest:cidr4' \
'tos:range(0,31)' \
'mark:string' \
'invert:bool' \
'lookup:or(range(0,65535),string)' \
'goto:range(0,65535)' \
'action:or("prohibit", "unreachable", "blackhole", "throw")'
}
validate_rule6_section()
{
uci_validate_section network rule6 "${1}" \
'in:string' \
'out:string' \
'src:cidr6' \
'dest:cidr6' \
'tos:range(0,31)' \
'mark:string' \
'invert:bool' \
'lookup:or(range(0,65535),string)' \
'goto:range(0,65535)' \
'action:or("prohibit", "unreachable", "blackhole", "throw")'
}
validate_switch_section()
{
uci_validate_section network switch "${1}" \
'name:string' \
'enable:bool' \
'enable_vlan:bool' \
'reset:bool' \
'ar8xxx_mib_poll_interval:uinteger' \
'ar8xxx_mib_type:range(0,1)'
}
validate_switch_vlan()
{
uci_validate_section network switch_vlan "${1}" \
'device:string' \
'vlan:uinteger' \
'ports:list(ports)'
}
service_triggers()
{
procd_add_reload_trigger network wireless
procd_open_validate
validate_atm_bridge_section
validate_route_section
[ -e /proc/sys/net/ipv6 ] && validate_route6_section
validate_rule_section
[ -e /proc/sys/net/ipv6 ] && validate_rule6_section
validate_switch_section
validate_switch_vlan
procd_close_validate
}
shutdown() {
ifdown -a
sleep 1
}
+25
View File
@@ -0,0 +1,25 @@
#!/bin/sh /etc/rc.common
START=25
USE_PROCD=1
start_service() {
reload_service
}
service_triggers() {
procd_add_reload_trigger "network"
procd_add_reload_trigger "firewall"
procd_add_raw_trigger "interface.*" 1000 /etc/init.d/packet_steering reload
}
reload_service() {
packet_steering="$(uci -q get "network.@globals[0].packet_steering")"
steering_flows="$(uci -q get "network.@globals[0].steering_flows")"
[ "${steering_flows:-0}" -gt 0 ] && opts="-l $steering_flows"
if [ -e "/usr/libexec/platform/packet-steering.sh" ]; then
/usr/libexec/platform/packet-steering.sh "$packet_steering"
else
/usr/libexec/network/packet-steering.uc $opts "$packet_steering"
fi
}
@@ -0,0 +1,23 @@
. /lib/functions.sh
migrate_release() {
local config="$1"
local proto
local release
config_get proto "$config" proto
config_get release "$config" release
[ "$proto" = "dhcp" ] && [ -n "$release" ] && {
norelease="$((!$release))"
uci_set network "$config" norelease "$norelease"
uci_remove network "$config" release
}
}
config_load network
config_foreach migrate_release interface
uci commit network
exit 0
+1
View File
@@ -0,0 +1 @@
# This script is sourced by udhcpc's dhcp.script at every DHCP event.
+120
View File
@@ -0,0 +1,120 @@
#!/bin/sh
[ -z "$1" ] && echo "Error: should be run by udhcpc" && exit 1
. /lib/functions.sh
. /lib/netifd/netifd-proto.sh
set_classless_routes() {
local max=128
while [ -n "$1" -a -n "$2" -a $max -gt 0 ]; do
proto_add_ipv4_route "${1%%/*}" "${1##*/}" "$2" "$ip"
max=$(($max-1))
shift 2
done
}
setup_interface () {
proto_init_update "*" 1
proto_add_ipv4_address "$ip" "${subnet:-255.255.255.0}"
# TODO: apply $broadcast
local ip_net IP PREFIX NETWORK NETMASK BROADCAST
ipcalc "$ip/$mask" && ip_net="$NETWORK"
local i
for i in $router; do
local gw_net
ipcalc "$i/$mask" && gw_net="$NETWORK"
[ "$ip_net" != "$gw_net" ] && proto_add_ipv4_route "$i" 32 "" "$ip"
proto_add_ipv4_route 0.0.0.0 0 "$i" "$ip"
local r
for r in $CUSTOMROUTES; do
proto_add_ipv4_route "${r%%/*}" "${r##*/}" "$i" "$ip"
done
done
# CIDR STATIC ROUTES (rfc3442)
[ -n "$staticroutes" ] && set_classless_routes $staticroutes
[ -n "$msstaticroutes" ] && set_classless_routes $msstaticroutes
for i in $dns; do
proto_add_dns_server "$i"
done
for i in $domain; do
proto_add_dns_search "$i"
done
# TODO: Deprecate timesvr in favor of timesrv
if [ -n "$timesvr" -a -z "$timesrv" ]; then
timesrv="$timesvr"
echo "Environment variable 'timesvr' will be deprecated; use 'timesrv' instead."
fi
proto_add_data
[ -n "$ZONE" ] && json_add_string zone "$ZONE"
[ -n "$ntpsrv" ] && json_add_string ntpserver "$ntpsrv"
[ -n "$timesrv" ] && json_add_string timeserver "$timesrv"
[ -n "$hostname" ] && json_add_string hostname "$hostname"
[ -n "$message" ] && json_add_string message "$message"
[ -n "$timezone" ] && json_add_int timezone "$timezone"
[ -n "$lease" ] && json_add_int leasetime "$lease"
[ -n "$serverid" ] && json_add_string dhcpserver "$serverid"
proto_close_data
proto_send_update "$INTERFACE"
if [ "$IFACE6RD" != 0 -a -n "$ip6rd" ]; then
local v4mask="${ip6rd%% *}"
ip6rd="${ip6rd#* }"
local ip6rdprefixlen="${ip6rd%% *}"
ip6rd="${ip6rd#* }"
local ip6rdprefix="${ip6rd%% *}"
ip6rd="${ip6rd#* }"
local ip6rdbr="${ip6rd%% *}"
[ -n "$ZONE" ] || ZONE=$(fw3 -q network $INTERFACE 2>/dev/null)
[ -z "$IFACE6RD" -o "$IFACE6RD" = 1 ] && IFACE6RD=${INTERFACE}_6
json_init
json_add_string name "$IFACE6RD"
json_add_string ifname "@$INTERFACE"
json_add_string proto "6rd"
json_add_string peeraddr "$ip6rdbr"
json_add_int ip4prefixlen "$v4mask"
json_add_string ip6prefix "$ip6rdprefix"
json_add_int ip6prefixlen "$ip6rdprefixlen"
json_add_string tunlink "$INTERFACE"
[ -n "$IFACE6RD_DELEGATE" ] && json_add_boolean delegate "$IFACE6RD_DELEGATE"
[ -n "$ZONE6RD" ] || ZONE6RD=$ZONE
[ -n "$ZONE6RD" ] && json_add_string zone "$ZONE6RD"
[ -n "$MTU6RD" ] && json_add_string mtu "$MTU6RD"
json_close_object
ubus call network add_dynamic "$(json_dump)"
fi
}
deconfig_interface() {
proto_init_update "*" 0
proto_send_update "$INTERFACE"
}
case "$1" in
deconfig)
deconfig_interface
;;
renew|bound)
setup_interface
;;
esac
# user rules
[ -f /etc/udhcpc.user ] && . /etc/udhcpc.user "$@"
for f in /etc/udhcpc.user.d/*; do
[ -f "$f" ] && (. "$f" "$@")
done
exit 0
+79
View File
@@ -0,0 +1,79 @@
import * as uci from "uci";
import * as uloop from "uloop";
import * as ubus from "ubus";
import { access, dirname } from "fs";
function ex_handler(e)
{
netifd.log(netifd.L_WARNING, `Exception: ${e}\n${e.stacktrace[0].context}\n`);
}
uloop.guard(ex_handler);
ubus.guard(ex_handler);
let wireless;
function uci_ctx()
{
let savedir = netifd.dummy_mode ? "./tmp" : null;
let ctx = uci.cursor(netifd.config_path, savedir, null, {
strict: false
});
return ctx;
}
function config_init()
{
let ctx = uci_ctx();
if (wireless)
wireless.config_init(ctx);
}
function config_start()
{
if (wireless)
wireless.config_start();
}
function check_interfaces()
{
if (wireless)
wireless.check_interfaces();
}
function hotplug(name, add)
{
if (wireless)
wireless.hotplug(name, add);
}
function ex_wrap(cb)
{
let fn = cb;
return (...args) => {
try {
return fn(...args);
} catch (e) {
netifd.log(netifd.L_WARNING, `${e}\n${e.stacktrace[0].context}`);
}
};
}
netifd.cb = {
hotplug: ex_wrap(hotplug),
config_init: ex_wrap(config_init),
config_start: ex_wrap(config_start),
check_interfaces: ex_wrap(check_interfaces),
};
const wireless_module = dirname(sourcepath()) + "/wireless.uc";
if (access(wireless_module, "r")) {
try {
wireless = loadfile(wireless_module)();
} catch (e) {
netifd.log(netifd.L_WARNING, `Error loading wireless module: ${e}\n${e.stacktrace[0].context}\n`);
}
} else {
netifd.log(netifd.L_WARNING, `Wireless module not found\n`);
}
+140
View File
@@ -0,0 +1,140 @@
#!/bin/sh
[ -x /sbin/udhcpc ] || exit 0
. /lib/functions.sh
. /lib/functions/network.sh
. ../netifd-proto.sh
. /lib/config/uci.sh
init_proto "$@"
proto_dhcp_init_config() {
renew_handler=1
proto_config_add_string 'ipaddr:ipaddr'
proto_config_add_string 'hostname:hostname'
proto_config_add_string clientid
proto_config_add_string sendclientid
proto_config_add_string vendorid
proto_config_add_boolean 'broadcast:bool'
proto_config_add_boolean 'norelease:bool'
proto_config_add_string 'reqopts:list(string)'
proto_config_add_boolean 'defaultreqopts:bool'
proto_config_add_string iface6rd
proto_config_add_array 'sendopts:list(string)'
proto_config_add_boolean delegate
proto_config_add_string zone6rd
proto_config_add_string zone
proto_config_add_string mtu6rd
proto_config_add_string customroutes
proto_config_add_boolean classlessroute
}
proto_dhcp_add_sendopts() {
[ -n "$1" ] && append "$3" "-x $1"
}
proto_dhcp_get_default_clientid() {
[ -z "$1" ] && return
local iface="$1"
local duid
local iaid
duid="$(uci_get network @globals[0] dhcp_default_duid)"
[ -n "$duid" ] && {
duid="$(hexdump_2hex "$duid")"
[ -z "$duid" ] && logger -p warn -t dhcp "$iface: ignoring invalid dhcp_default_duid value"
}
[ -z "$duid" ] && return
network_generate_iface_iaid iaid "$iface"
printf "ff%s%s" "$iaid" "$duid"
}
proto_dhcp_setup() {
local config="$1"
local iface="$2"
local ipaddr hostname clientid sendclientid vendorid broadcast norelease reqopts defaultreqopts iface6rd sendopts delegate zone6rd zone mtu6rd customroutes classlessroute
json_get_vars ipaddr hostname clientid sendclientid vendorid broadcast norelease reqopts defaultreqopts iface6rd delegate zone6rd zone mtu6rd customroutes classlessroute
local opt dhcpopts
for opt in $reqopts; do
append dhcpopts "-O $opt"
done
json_for_each_item proto_dhcp_add_sendopts sendopts dhcpopts
[ -z "$hostname" ] && hostname="$(cat /proc/sys/kernel/hostname)"
[ "$hostname" = "*" ] && hostname=
[ "$defaultreqopts" = 0 ] && defaultreqopts="-o" || defaultreqopts=
[ "$broadcast" = 1 ] && broadcast="-B" || broadcast=
[ "$norelease" = 1 ] && norelease="" || norelease="-R"
case "$sendclientid" in
global)
clientid="$(proto_dhcp_get_default_clientid "$iface")"
;;
hardware)
clientid=''
;;
none)
clientid='-C'
;;
auto|\
*)
[ -n "$clientid" ] && {
clientid="$(hexdump_2hex "$clientid")"
[ -z "$clientid" ] && {
logger -p warn -t dhcp "$iface: ignoring invalid clientid value"
}
}
[ -z "$clientid" ] && clientid="$(proto_dhcp_get_default_clientid "$iface")"
;;
esac
[ -n "${clientid##-C}" ] && clientid="-x 0x3d:$clientid"
[ -n "$vendorid" ] && append dhcpopts "-x 0x3c:$(echo -n "$vendorid" | hexdump -ve '1/1 "%02x"')"
[ -n "$iface6rd" ] && proto_export "IFACE6RD=$iface6rd"
[ "$iface6rd" != 0 -a -f /lib/netifd/proto/6rd.sh ] && append dhcpopts "-O 212"
[ -n "$zone6rd" ] && proto_export "ZONE6RD=$zone6rd"
[ -n "$zone" ] && proto_export "ZONE=$zone"
[ -n "$mtu6rd" ] && proto_export "MTU6RD=$mtu6rd"
[ -n "$customroutes" ] && proto_export "CUSTOMROUTES=$customroutes"
[ "$delegate" = "0" ] && proto_export "IFACE6RD_DELEGATE=0"
# Request classless route option (see RFC 3442) by default
[ "$classlessroute" = "0" ] || append dhcpopts "-O 121"
# Avoid sending duplicate Option 60 values
local emptyvendorid
case "$dhcpopts" in
*"-x 0"[xX]*"3"[cC]":"* |\
*"-x 60:"* |\
*"-x vendor:"*)
emptyvendorid=1
;;
esac
proto_export "INTERFACE=$config"
proto_run_command "$config" udhcpc \
-p /var/run/udhcpc-$iface.pid \
-s /lib/netifd/dhcp.script \
-f -t 0 -i "$iface" \
${ipaddr:+-r ${ipaddr/\/*/}} \
${hostname:+-x "hostname:$hostname"} \
${emptyvendorid:+-V ""} \
$clientid $defaultreqopts $broadcast $norelease $dhcpopts
}
proto_dhcp_renew() {
local interface="$1"
# SIGUSR1 forces udhcpc to renew its lease
local sigusr1="$(kill -l SIGUSR1)"
[ -n "$sigusr1" ] && proto_kill_command "$interface" $sigusr1
}
proto_dhcp_teardown() {
local interface="$1"
proto_kill_command "$interface"
}
add_protocol dhcp
+131
View File
@@ -0,0 +1,131 @@
'use strict';
import { glob, basename, realpath, chdir, mkstemp } from "fs";
export const TYPE_ARRAY = 1;
export const TYPE_STRING = 3;
export const TYPE_INT = 5;
export const TYPE_BOOL = 7;
export function parse_bool(val)
{
switch (val) {
case "1":
case "true":
return true;
case "0":
case "false":
return false;
}
};
export function parse_array(val)
{
if (type(val) != "array")
val = split(val, /\s+/);
return val;
};
function __type_parsers()
{
let ret = [];
ret[TYPE_ARRAY] = parse_array;
ret[TYPE_STRING] = function(val) {
return val;
};
ret[TYPE_INT] = function(val) {
return +val;
};
ret[TYPE_BOOL] = parse_bool;
return ret;
}
export const type_parser = __type_parsers();
export function handler_load(path, cb)
{
for (let script in glob(path + "/*.sh")) {
script = basename(script);
let f = mkstemp();
let prev_dir = realpath(".");
chdir(path);
system(`./${script} "" "dump" >&${f.fileno()}`);
chdir(prev_dir);
f.seek();
while (!f.error()) {
let data = trim(f.read("line"));
try {
data = json(data);
} catch (e) {
continue;
}
if (type(data) != "object")
continue;
cb(script, data);
}
f.close();
}
};
export function handler_attributes(data, extra, validate)
{
let ret = { ...extra };
for (let cur in data) {
let name_data = split(cur[0], ":", 2);
let name = name_data[0];
ret[name] = cur[1];
if (validate && name_data[1])
validate[name] = name_data[1];
}
return ret;
};
export function parse_attribute_list(data, spec)
{
let ret = {};
for (let name, type_id in spec) {
if (!(name in data))
continue;
let val = data[name];
let parser = type_parser[type_id];
if (parser)
val = parser(val);
ret[name] = val;
}
return ret;
};
export function is_equal(val1, val2) {
let t1 = type(val1);
if (t1 != type(val2))
return false;
if (t1 == "array") {
if (length(val1) != length(val2))
return false;
for (let i = 0; i < length(val1); i++)
if (!is_equal(val1[i], val2[i]))
return false;
return true;
} else if (t1 == "object") {
for (let key in val1)
if (!is_equal(val1[key], val2[key]))
return false;
for (let key in val2)
if (val1[key] == null)
return false;
return true;
} else {
return val1 == val2;
}
};
+76
View File
@@ -0,0 +1,76 @@
#!/bin/sh
# Copyright (C) 2011 OpenWrt.org
. /usr/share/libubox/jshn.sh
find_config() {
local device="$1"
local ifdev ifl3dev ifobj
for ifobj in $(ubus list network.interface.\*); do
interface="${ifobj##network.interface.}"
(
json_load "$(ifstatus $interface)"
json_get_var ifdev device
json_get_var ifl3dev l3_device
if [ "$device" = "$ifdev" ] || [ "$device" = "$ifl3dev" ]; then
echo "$interface"
exit 0
else
exit 1
fi
) && return
done
}
unbridge() {
return
}
ubus_call() {
json_init
local _data="$(ubus -S call "$1" "$2")"
[ -z "$_data" ] && return 1
json_load "$_data"
return 0
}
fixup_interface() {
local config="$1"
local ifname type device l3dev
config_get type "$config" type
config_get ifname "$config" ifname
[ "bridge" = "$type" ] && ifname="br-$config"
ubus_call "network.interface.$config" status || return 0
json_get_var l3dev l3_device
[ -n "$l3dev" ] && ifname="$l3dev"
json_init
config_set "$config" ifname "$ifname"
}
scan_interfaces() {
config_load network
config_foreach fixup_interface interface
}
prepare_interface_bridge() {
local config="$1"
[ -n "$config" ] || return 0
ubus call network.interface."$config" prepare
}
setup_interface() {
local iface="$1"
local config="$2"
[ -n "$config" ] || return 0
ubus call network.interface."$config" add_device "{ \"name\": \"$iface\" }"
}
do_sysctl() {
[ -n "$2" ] && \
sysctl -n -e -w "$1=$2" >/dev/null || \
sysctl -n -e "$1"
}
+12
View File
@@ -0,0 +1,12 @@
#!/bin/sh
. /usr/share/libubox/jshn.sh
DEVICE="$1"
[ -n "$DEVICE" ] || {
echo "Usage: $0 <device>"
exit 1
}
json_init
json_add_string name "$DEVICE"
ubus call network.device status "$(json_dump)"
+1
View File
@@ -0,0 +1 @@
ifup
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
INTERFACE="$1"
[ -n "$INTERFACE" ] || {
echo "Usage: $0 <interface>"
exit 1
}
ubus -S list "network.interface.$INTERFACE" >/dev/null || {
echo "Interface $INTERFACE not found"
exit 1
}
ubus call network.interface status "{ \"interface\" : \"$INTERFACE\" }"
+44
View File
@@ -0,0 +1,44 @@
#!/bin/sh
ifup_all=
if_call() {
local interface="$1"
for mode in $modes; do
ubus call network.interface $mode "{ \"interface\" : \"$interface\" }"
done
}
case "$0" in
*ifdown) modes=down;;
*ifup)
modes="down up"
;;
*) echo "Invalid command: $0";;
esac
while :; do
case "$1" in
-a)
ifup_all=1
shift
;;
*)
break
;;
esac
done
[ "$modes" = "down up" ] && ubus call network reload
if [ -n "$ifup_all" ]; then
for interface in $(ubus -S list 'network.interface.*'); do
if_call "${interface##network.interface.}"
done
exit
else
ubus -S list "network.interface.$1" > /dev/null || {
echo "Interface $1 not found"
exit
}
if_call "$1"
fi
+279
View File
@@ -0,0 +1,279 @@
#!/usr/bin/env ucode
'use strict';
import { glob, basename, dirname, readlink, readfile, realpath, writefile, error, open } from "fs";
let napi_weight = 1.0;
let cpu_thread_weight = 0.75;
let rx_weight = 0.75;
let eth_bias = 2.0;
let debug = 0, do_nothing = 0;
let disable;
let cpus;
let all_cpus;
let local_flows = 0;
while (length(ARGV) > 0) {
let arg = shift(ARGV);
switch (arg) {
case "-d":
debug++;
break;
case "-n":
do_nothing++;
break;
case '0':
disable = true;
break;
case '2':
all_cpus = true;
break;
case '-l':
local_flows = +shift(ARGV);
break;
}
}
function task_name(pid)
{
let stat = open(`/proc/${pid}/status`, "r");
if (!stat)
return;
let line = stat.read("line");
stat.close();
return trim(split(line, "\t", 2)[1]);
}
function set_task_cpu(pid, cpu) {
if (disable)
cpu = join(",", map(cpus, (cpu) => cpu.id));
let name = task_name(pid);
if (!name)
return;
if (debug || do_nothing)
warn(`taskset -p -c ${cpu} ${name}\n`);
if (!do_nothing)
system(`taskset -p -c ${cpu} ${pid}`);
}
function cpu_mask(cpu)
{
let mask;
if (cpu < 0)
mask = (1 << length(cpus)) - 1;
else
mask = (1 << int(cpu));
return sprintf("%x", mask);
}
function set_netdev_cpu(dev, cpu, rx_queue) {
rx_queue ??= "rx-*";
let queues = glob(`/sys/class/net/${dev}/queues/${rx_queue}/rps_cpus`);
let val = cpu_mask(cpu);
if (disable)
val = 0;
for (let queue in queues) {
if (debug || do_nothing)
warn(`echo ${val} > ${queue}\n`);
if (!do_nothing)
writefile(queue, `${val}`);
}
queues = glob(`/sys/class/net/${dev}/queues/${rx_queue}/rps_flow_cnt`);
for (let queue in queues) {
if (debug || do_nothing)
warn(`echo ${local_flows} > ${queue}\n`);
if (!do_nothing)
writefile(queue, `${local_flows}`);
}
}
function task_device_match(name, device)
{
let napi_match = match(name, /napi\/([^-]*)-\d+/);
if (!napi_match)
napi_match = match(name, /mt76-tx (phy\d+)/);
if (napi_match &&
(index(device.phy, napi_match[1]) >= 0 ||
index(device.netdev, napi_match[1]) >= 0))
return true;
if (device.driver == "mtk_soc_eth" && match(name, /napi\/mtk_eth-/))
return true;
return false;
}
cpus = map(glob("/sys/bus/cpu/devices/*"), (path) => {
return {
id: int(match(path, /.*cpu(\d+)/)[1]),
core: int(trim(readfile(`${path}/topology/core_id`))),
load: 0.0,
};
});
cpus = slice(cpus, 0, 64);
if (length(cpus) < 2)
exit(0);
function cpu_add_weight(cpu_id, weight)
{
let cpu = cpus[cpu_id];
cpu.load += weight;
for (let sibling in cpus) {
if (sibling == cpu || sibling.core != cpu.core)
continue;
sibling.load += weight * cpu_thread_weight;
}
}
function get_next_cpu(weight, prev_cpu)
{
if (disable)
return 0;
let sort_cpus = sort(slice(cpus), (a, b) => a.load - b.load);
let idx = 0;
if (prev_cpu != null && sort_cpus[idx].id == prev_cpu)
idx++;
let cpu = sort_cpus[idx].id;
cpu_add_weight(cpu, weight);
return cpu;
}
let phys_devs = {};
let netdev_phys = {};
let netdevs = map(glob("/sys/class/net/*"), (dev) => basename(dev));
for (let dev in netdevs) {
let pdev_path = realpath(`/sys/class/net/${dev}/device`);
if (!pdev_path)
continue;
if (length(glob(`/sys/class/net/${dev}/lower_*`)) > 0)
continue;
let pdev = phys_devs[pdev_path];
if (!pdev) {
pdev = phys_devs[pdev_path] = {
path: pdev_path,
driver: basename(readlink(`${pdev_path}/driver`)),
netdev: [],
phy: [],
tasks: [],
rx_tasks: [],
rx_queues: map(glob(`/sys/class/net/${dev}/queues/rx-*/rps_cpus`),
(v) => basename(dirname(v))),
};
}
let phyidx = trim(readfile(`/sys/class/net/${dev}/phy80211/index`));
if (phyidx != null) {
let phy = `phy${phyidx}`;
if (index(pdev.phy, phy) < 0)
push(pdev.phy, phy);
}
push(pdev.netdev, dev);
netdev_phys[dev] = pdev;
}
for (let path in glob("/proc/*/exe")) {
readlink(path);
if (error() != "No such file or directory")
continue;
let pid = basename(dirname(path));
let name = task_name(pid);
for (let devname in phys_devs) {
let dev = phys_devs[devname];
if (!task_device_match(name, dev))
continue;
push(dev.tasks, pid);
let napi_match = match(name, /napi\/([^-]*)-(\d+)/);
if (napi_match && napi_match[2] > 0)
push(dev.rx_tasks, pid);
break;
}
}
function assign_dev_queues_cpu(dev) {
let num = length(dev.rx_queues);
if (num < length(dev.rx_tasks))
num = length(dev.rx_tasks);
for (let i = 0; i < num; i++) {
let cpu;
let task = dev.rx_tasks[i];
if (num >= length(cpus))
cpu = i % length(cpus);
else if (task)
cpu = get_next_cpu(napi_weight);
else
cpu = -1;
set_task_cpu(task, cpu);
let rxq = dev.rx_queues[i];
if (!rxq)
continue;
if (num >= length(cpus))
cpu = (i + 1) % length(cpus);
else if (all_cpus)
cpu = -1;
else
cpu = get_next_cpu(napi_weight, cpu);
for (let netdev in dev.netdev)
set_netdev_cpu(netdev, cpu, rxq);
}
}
function assign_dev_cpu(dev) {
if (length(dev.rx_queues) > 1 &&
length(dev.rx_tasks) > 1)
return assign_dev_queues_cpu(dev);
if (length(dev.tasks) > 0) {
let cpu = dev.napi_cpu = get_next_cpu(napi_weight);
for (let task in dev.tasks)
set_task_cpu(task, cpu);
}
if (length(dev.netdev) > 0) {
let cpu;
if (all_cpus)
cpu = -1;
else
cpu = get_next_cpu(rx_weight, dev.napi_cpu);
for (let netdev in dev.netdev)
set_netdev_cpu(netdev, cpu);
}
}
// Assign ethernet devices first
for (let devname in phys_devs) {
let dev = phys_devs[devname];
if (!length(dev.phy))
assign_dev_cpu(dev);
}
// Add bias to avoid assigning other tasks to CPUs with ethernet NAPI
for (let devname in phys_devs) {
let dev = phys_devs[devname];
if (!length(dev.tasks) || dev.napi_cpu == null)
continue;
cpu_add_weight(dev.napi_cpu, eth_bias);
}
// Assign WLAN devices
for (let devname in phys_devs) {
let dev = phys_devs[devname];
if (length(dev.phy) > 0)
assign_dev_cpu(dev);
}
if (debug > 1)
warn(sprintf("devices: %.J\ncpus: %.J\n", phys_devs, cpus));
+57
View File
@@ -0,0 +1,57 @@
#!/bin/sh
[ -z "$1" ] && echo "Error: should be run by udhcpc" && exit 1
set_classless_routes() {
local max=128
local type
while [ -n "$1" -a -n "$2" -a $max -gt 0 ]; do
[ ${1##*/} -eq 32 ] && type=host || type=net
echo "udhcpc: adding route for $type $1 via $2"
route add -$type "$1" gw "$2" dev "$interface"
max=$(($max-1))
shift 2
done
}
setup_interface() {
echo "udhcpc: ip addr add $ip/${subnet:-255.255.255.0} broadcast ${broadcast:-+} dev $interface"
ip addr add $ip/${subnet:-255.255.255.0} broadcast ${broadcast:-+} dev $interface
[ -n "$router" ] && [ "$router" != "0.0.0.0" ] && [ "$router" != "255.255.255.255" ] && {
echo "udhcpc: setting default routers: $router"
local valid_gw=""
for i in $router ; do
route add default gw $i dev $interface
valid_gw="${valid_gw:+$valid_gw|}$i"
done
eval $(route -n | awk '
/^0.0.0.0\W{9}('$valid_gw')\W/ {next}
/^0.0.0.0/ {print "route del -net "$1" gw "$2";"}
')
}
# CIDR STATIC ROUTES (rfc3442)
[ -n "$staticroutes" ] && set_classless_routes $staticroutes
[ -n "$msstaticroutes" ] && set_classless_routes $msstaticroutes
}
applied=
case "$1" in
deconfig)
ip -4 addr flush dev "$interface"
;;
renew)
setup_interface update
;;
bound)
setup_interface ifup
;;
esac
# user rules
[ -f /etc/udhcpc.user ] && . /etc/udhcpc.user
exit 0
@@ -0,0 +1,23 @@
From a349ea7c5f73513808ea266b823e915344d9bc69 Mon Sep 17 00:00:00 2001
From: kiddin9 <48883331+kiddin9@users.noreply.github.com>
Date: Fri, 18 Sep 2026 06:05:11 +0800
Subject: [PATCH] Update proto.c
Signed-off-by: kiddin9 <48883331+kiddin9@users.noreply.github.com>
---
proto.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/proto.c b/proto.c
index c338ff27..6c93127c 100644
--- a/proto.c
+++ b/proto.c
@@ -413,7 +413,7 @@ proto_apply_static_ip_settings(struct interface *iface, struct blob_attr *attr)
struct blob_attr *tb[__OPT_MAX];
struct blob_attr *cur;
const char *error;
- unsigned int netmask = 32;
+ unsigned int netmask = 24;
bool ip6deprecated;
int n_v4 = 0, n_v6 = 0;
struct in_addr bcast = {0,}, ptp = {0,};