mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2026-06-11 04:37:26 -04:00
Compare commits
4 Commits
f2a532ec09
...
9d78b2f53c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d78b2f53c | ||
|
|
50d3d287e4 | ||
|
|
41bc454602 | ||
|
|
c504cbd47c |
@@ -1,13 +1,13 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=netifd
|
||||
PKG_RELEASE:=3
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL=$(PROJECT_GIT)/project/netifd.git
|
||||
PKG_SOURCE_DATE:=2025-10-20
|
||||
PKG_SOURCE_VERSION:=777f5942fa7d6245f6ad29daa1daecc400344d37
|
||||
PKG_MIRROR_HASH:=7fc56f436faa1a5b05a147febed52c6f58c479125a38c9737736b352cd8d4409
|
||||
PKG_SOURCE_DATE:=2026-02-15
|
||||
PKG_SOURCE_VERSION:=c6122254eb7003377b67a6ad14d284b69725bbee
|
||||
PKG_MIRROR_HASH:=feb62dba4dfecba1e3e758e5e4e822e9776b4104ee133dccfc3feb7ae6c5182d
|
||||
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as uci from "uci";
|
||||
import * as uloop from "uloop";
|
||||
import * as ubus from "ubus";
|
||||
import * as libubus from "ubus";
|
||||
import { access, dirname } from "fs";
|
||||
|
||||
function ex_handler(e)
|
||||
@@ -9,9 +9,11 @@ function ex_handler(e)
|
||||
}
|
||||
|
||||
uloop.guard(ex_handler);
|
||||
ubus.guard(ex_handler);
|
||||
libubus.guard(ex_handler);
|
||||
|
||||
let ubus = netifd.ubus = libubus.connect();
|
||||
let wireless;
|
||||
let proto_mod;
|
||||
|
||||
function uci_ctx()
|
||||
{
|
||||
@@ -26,8 +28,13 @@ function config_init()
|
||||
{
|
||||
let ctx = uci_ctx();
|
||||
|
||||
if (wireless)
|
||||
wireless.config_init(ctx);
|
||||
for (let mod in [ wireless, proto_mod ]) {
|
||||
try {
|
||||
mod?.config_init(ctx);
|
||||
} catch (e) {
|
||||
netifd.log(netifd.L_WARNING, `${e}\n${e.stacktrace[0].context}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function config_start()
|
||||
@@ -77,3 +84,12 @@ if (access(wireless_module, "r")) {
|
||||
} else {
|
||||
netifd.log(netifd.L_WARNING, `Wireless module not found\n`);
|
||||
}
|
||||
|
||||
const proto_module = dirname(sourcepath()) + "/proto.uc";
|
||||
if (access(proto_module, "r")) {
|
||||
try {
|
||||
proto_mod = loadfile(proto_module)();
|
||||
} catch (e) {
|
||||
netifd.log(netifd.L_WARNING, `Error loading proto module: ${e}\n${e.stacktrace[0].context}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
112
package/network/config/netifd/files/lib/netifd/proto-ucode.uc
Normal file
112
package/network/config/netifd/files/lib/netifd/proto-ucode.uc
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env ucode
|
||||
|
||||
import * as libubus from "ubus";
|
||||
|
||||
let script_path = ARGV[0];
|
||||
let proto_name = ARGV[1];
|
||||
let action = ARGV[2];
|
||||
let iface_name = ARGV[3];
|
||||
let config_json = ARGV[4];
|
||||
let device = ARGV[5];
|
||||
|
||||
let config;
|
||||
try {
|
||||
let blob = json(config_json);
|
||||
let inner = blob?._ucode_config;
|
||||
config = inner ? json(inner) : blob;
|
||||
} catch (e) {
|
||||
warn(`Failed to parse config JSON: ${e}\n${e.stacktrace[0].context}\n`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let ubus = libubus.connect();
|
||||
if (!ubus) {
|
||||
warn(`Failed to connect to ubus\n`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let notify_path = `network.interface.${iface_name}`;
|
||||
|
||||
function proto_notify(data)
|
||||
{
|
||||
return ubus.call(notify_path, "notify_proto", data);
|
||||
}
|
||||
|
||||
let proto = {
|
||||
iface: iface_name,
|
||||
proto: proto_name,
|
||||
config,
|
||||
device,
|
||||
|
||||
notify: proto_notify,
|
||||
|
||||
update_link: function(up, data) {
|
||||
let msg = { action: 0, "link-up": up, ...(data ?? {}) };
|
||||
return proto_notify(msg);
|
||||
},
|
||||
|
||||
run_command: function(argv, env) {
|
||||
let msg = { action: 1, command: argv };
|
||||
if (env)
|
||||
msg.env = env;
|
||||
return proto_notify(msg);
|
||||
},
|
||||
|
||||
kill_command: function(signal) {
|
||||
return proto_notify({ action: 2, signal: signal ?? 15 });
|
||||
},
|
||||
|
||||
error: function(errors) {
|
||||
return proto_notify({ action: 3, error: errors });
|
||||
},
|
||||
|
||||
block_restart: function() {
|
||||
return proto_notify({ action: 4 });
|
||||
},
|
||||
|
||||
set_available: function(available) {
|
||||
return proto_notify({ action: 5, available });
|
||||
},
|
||||
|
||||
add_host_dependency: function(host, ifname) {
|
||||
let msg = { action: 6 };
|
||||
if (host)
|
||||
msg.host = host;
|
||||
if (ifname)
|
||||
msg.ifname = ifname;
|
||||
return proto_notify(msg);
|
||||
},
|
||||
|
||||
setup_failed: function() {
|
||||
return proto_notify({ action: 7 });
|
||||
},
|
||||
};
|
||||
|
||||
let handlers = {};
|
||||
|
||||
let netifd_stub = {
|
||||
add_proto: function(handler) {
|
||||
if (handler?.name)
|
||||
handlers[handler.name] = handler;
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
include(script_path, { netifd: netifd_stub });
|
||||
} catch (e) {
|
||||
warn(`Failed to load proto handler script '${script_path}': ${e}\n${e.stacktrace[0].context}\n`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let handler = handlers[proto_name];
|
||||
if (!handler) {
|
||||
warn(`No handler found for protocol '${proto_name}'\n`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!handler[action]) {
|
||||
warn(`Handler '${proto_name}' has no '${action}' function\n`);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
handler[action](proto);
|
||||
52
package/network/config/netifd/files/lib/netifd/proto.uc
Normal file
52
package/network/config/netifd/files/lib/netifd/proto.uc
Normal file
@@ -0,0 +1,52 @@
|
||||
import { sorted_json } from "./utils.uc";
|
||||
import { dirname, glob } from "fs";
|
||||
|
||||
let ctx;
|
||||
|
||||
function proto_config_load(config_fn, section_name)
|
||||
{
|
||||
if (!ctx)
|
||||
return null;
|
||||
|
||||
let section_data = ctx.get_all("network", section_name);
|
||||
if (!section_data)
|
||||
return null;
|
||||
|
||||
let config_obj = {
|
||||
iface: section_name,
|
||||
section: section_name,
|
||||
data: section_data,
|
||||
uci: ctx,
|
||||
};
|
||||
|
||||
let result;
|
||||
if (config_fn)
|
||||
result = config_fn(config_obj);
|
||||
else
|
||||
result = section_data;
|
||||
|
||||
return sorted_json(result);
|
||||
}
|
||||
|
||||
netifd.cb.proto_config_load = proto_config_load;
|
||||
|
||||
let base = dirname(sourcepath());
|
||||
for (let script in glob(base + "/proto/*.uc")) {
|
||||
try {
|
||||
loadfile(script)();
|
||||
} catch (e) {
|
||||
netifd.log(netifd.L_WARNING,
|
||||
`Error loading proto handler ${script}: ${e}\n${e.stacktrace[0].context}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
function config_init(uci)
|
||||
{
|
||||
ctx = uci;
|
||||
if (!ctx.load("network"))
|
||||
netifd.log(netifd.L_WARNING, `Failed to load network config\n`);
|
||||
}
|
||||
|
||||
return {
|
||||
config_init,
|
||||
};
|
||||
@@ -102,6 +102,26 @@ export function parse_attribute_list(data, spec)
|
||||
return ret;
|
||||
};
|
||||
|
||||
export function sorted_json(value) {
|
||||
let t = type(value);
|
||||
|
||||
if (t == "object") {
|
||||
let parts = [];
|
||||
for (let key in sort(keys(value)))
|
||||
push(parts, sprintf("%J", key) + ":" + sorted_json(value[key]));
|
||||
return "{" + join(",", parts) + "}";
|
||||
}
|
||||
|
||||
if (t == "array") {
|
||||
let parts = [];
|
||||
for (let item in value)
|
||||
push(parts, sorted_json(item));
|
||||
return "[" + join(",", parts) + "]";
|
||||
}
|
||||
|
||||
return sprintf("%J", value);
|
||||
};
|
||||
|
||||
export function is_equal(val1, val2) {
|
||||
let t1 = type(val1);
|
||||
|
||||
|
||||
@@ -58,7 +58,16 @@
|
||||
static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
|
||||
struct nl80211_wiphy_data *w)
|
||||
{
|
||||
@@ -6236,7 +6254,7 @@ const char * nl80211_iftype_str(enum nl8
|
||||
@@ -2427,6 +2445,8 @@ static void * wpa_driver_nl80211_drv_ini
|
||||
p2p_mode))
|
||||
goto failed;
|
||||
|
||||
+ bss->radio_mask = nl80211_get_radio_mask(bss);
|
||||
+
|
||||
if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_TX_STATUS) {
|
||||
drv->control_port_ap = 1;
|
||||
goto skip_wifi_status;
|
||||
@@ -6236,7 +6256,7 @@ const char * nl80211_iftype_str(enum nl8
|
||||
static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
|
||||
const char *ifname,
|
||||
enum nl80211_iftype iftype,
|
||||
@@ -67,7 +76,7 @@
|
||||
int (*handler)(struct nl_msg *, void *),
|
||||
void *arg)
|
||||
{
|
||||
@@ -6256,6 +6274,10 @@ static int nl80211_create_iface_once(str
|
||||
@@ -6256,6 +6276,10 @@ static int nl80211_create_iface_once(str
|
||||
if (wds && nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
|
||||
goto fail;
|
||||
|
||||
@@ -78,7 +87,7 @@
|
||||
/*
|
||||
* Tell cfg80211 that the interface belongs to the socket that created
|
||||
* it, and the interface should be deleted when the socket is closed.
|
||||
@@ -6311,14 +6333,14 @@ static int nl80211_create_iface_once(str
|
||||
@@ -6311,14 +6335,14 @@ static int nl80211_create_iface_once(str
|
||||
|
||||
int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
|
||||
const char *ifname, enum nl80211_iftype iftype,
|
||||
@@ -96,7 +105,7 @@
|
||||
|
||||
/* if error occurred and interface exists already */
|
||||
if (ret < 0 && if_nametoindex(ifname)) {
|
||||
@@ -6344,7 +6366,7 @@ int nl80211_create_iface(struct wpa_driv
|
||||
@@ -6344,7 +6368,7 @@ int nl80211_create_iface(struct wpa_driv
|
||||
|
||||
/* Try to create the interface again */
|
||||
ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
|
||||
@@ -105,7 +114,7 @@
|
||||
}
|
||||
|
||||
if (ret >= 0 && is_p2p_net_interface(iftype)) {
|
||||
@@ -8688,8 +8710,8 @@ static int i802_set_wds_sta(void *priv,
|
||||
@@ -8688,8 +8712,8 @@ static int i802_set_wds_sta(void *priv,
|
||||
if (!if_nametoindex(name)) {
|
||||
if (nl80211_create_iface(drv, name,
|
||||
NL80211_IFTYPE_AP_VLAN,
|
||||
@@ -116,7 +125,7 @@
|
||||
return -1;
|
||||
|
||||
if (bridge_ifname)
|
||||
@@ -9060,7 +9082,8 @@ static int wpa_driver_nl80211_if_add(voi
|
||||
@@ -9060,7 +9084,8 @@ static int wpa_driver_nl80211_if_add(voi
|
||||
|
||||
os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
|
||||
ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
|
||||
@@ -126,7 +135,7 @@
|
||||
&p2pdev_info, use_existing);
|
||||
if (!p2pdev_info.wdev_id_set || ifidx != 0) {
|
||||
wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s",
|
||||
@@ -9077,7 +9100,8 @@ static int wpa_driver_nl80211_if_add(voi
|
||||
@@ -9077,7 +9102,8 @@ static int wpa_driver_nl80211_if_add(voi
|
||||
(long long unsigned int) p2pdev_info.wdev_id);
|
||||
} else {
|
||||
ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
|
||||
@@ -136,7 +145,7 @@
|
||||
if (use_existing && ifidx == -ENFILE) {
|
||||
added = 0;
|
||||
ifidx = if_nametoindex(ifname);
|
||||
@@ -9147,6 +9171,8 @@ static int wpa_driver_nl80211_if_add(voi
|
||||
@@ -9147,6 +9173,8 @@ static int wpa_driver_nl80211_if_add(voi
|
||||
new_bss->ctx = bss_ctx;
|
||||
new_bss->added_if = added;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Tested-by: Simon Wunderlich <simon.wunderlich@openmesh.com>
|
||||
struct wpa_driver_set_key_params {
|
||||
--- a/src/drivers/driver_nl80211.c
|
||||
+++ b/src/drivers/driver_nl80211.c
|
||||
@@ -12376,6 +12376,18 @@ static int nl80211_put_mesh_id(struct nl
|
||||
@@ -12378,6 +12378,18 @@ static int nl80211_put_mesh_id(struct nl
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ Tested-by: Simon Wunderlich <simon.wunderlich@openmesh.com>
|
||||
static int nl80211_put_mesh_config(struct nl_msg *msg,
|
||||
struct wpa_driver_mesh_bss_params *params)
|
||||
{
|
||||
@@ -12437,6 +12449,7 @@ static int nl80211_join_mesh(struct i802
|
||||
@@ -12439,6 +12451,7 @@ static int nl80211_join_mesh(struct i802
|
||||
nl80211_put_basic_rates(msg, params->basic_rates) ||
|
||||
nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
|
||||
nl80211_put_beacon_int(msg, params->beacon_int) ||
|
||||
|
||||
@@ -720,7 +720,7 @@ as adding/removing interfaces.
|
||||
nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
|
||||
no_seq_check, NULL);
|
||||
nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
|
||||
@@ -8854,6 +8875,7 @@ static void *i802_init(struct hostapd_da
|
||||
@@ -8856,6 +8877,7 @@ static void *i802_init(struct hostapd_da
|
||||
char master_ifname[IFNAMSIZ];
|
||||
int ifindex, br_ifindex = 0;
|
||||
int br_added = 0;
|
||||
@@ -728,7 +728,7 @@ as adding/removing interfaces.
|
||||
|
||||
bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
|
||||
params->global_priv, 1,
|
||||
@@ -8914,21 +8936,17 @@ static void *i802_init(struct hostapd_da
|
||||
@@ -8916,21 +8938,17 @@ static void *i802_init(struct hostapd_da
|
||||
(params->num_bridge == 0 || !params->bridge[0]))
|
||||
add_ifidx(drv, br_ifindex, drv->ifindex);
|
||||
|
||||
@@ -760,7 +760,7 @@ as adding/removing interfaces.
|
||||
}
|
||||
|
||||
if (drv->capa.flags2 & WPA_DRIVER_FLAGS2_CONTROL_PORT_RX) {
|
||||
@@ -9310,6 +9328,50 @@ static int wpa_driver_nl80211_if_remove(
|
||||
@@ -9312,6 +9330,50 @@ static int wpa_driver_nl80211_if_remove(
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -811,7 +811,7 @@ as adding/removing interfaces.
|
||||
|
||||
static int cookie_handler(struct nl_msg *msg, void *arg)
|
||||
{
|
||||
@@ -11195,6 +11257,37 @@ static bool nl80211_is_drv_shared(void *
|
||||
@@ -11197,6 +11259,37 @@ static bool nl80211_is_drv_shared(void *
|
||||
#endif /* CONFIG_IEEE80211BE */
|
||||
|
||||
|
||||
@@ -849,7 +849,7 @@ as adding/removing interfaces.
|
||||
static int driver_nl80211_send_mlme(void *priv, const u8 *data,
|
||||
size_t data_len, int noack,
|
||||
unsigned int freq,
|
||||
@@ -15014,6 +15107,8 @@ const struct wpa_driver_ops wpa_driver_n
|
||||
@@ -15016,6 +15109,8 @@ const struct wpa_driver_ops wpa_driver_n
|
||||
.set_acl = wpa_driver_nl80211_set_acl,
|
||||
.if_add = wpa_driver_nl80211_if_add,
|
||||
.if_remove = driver_nl80211_if_remove,
|
||||
|
||||
@@ -123,7 +123,7 @@ untagged DHCP packets
|
||||
* get_wowlan - Get wake-on-wireless status
|
||||
--- a/src/drivers/driver_nl80211.c
|
||||
+++ b/src/drivers/driver_nl80211.c
|
||||
@@ -12896,7 +12896,7 @@ static const char * drv_br_net_param_str
|
||||
@@ -12898,7 +12898,7 @@ static const char * drv_br_net_param_str
|
||||
|
||||
|
||||
static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
|
||||
@@ -132,7 +132,7 @@ untagged DHCP packets
|
||||
{
|
||||
struct i802_bss *bss = priv;
|
||||
char path[128];
|
||||
@@ -12922,8 +12922,11 @@ static int wpa_driver_br_set_net_param(v
|
||||
@@ -12924,8 +12924,11 @@ static int wpa_driver_br_set_net_param(v
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ Hotfix-by: Sebastian Gottschall https://github.com/mirror/dd-wrt/commit/0c3001a6
|
||||
* send_action - Transmit an Action frame
|
||||
--- a/src/drivers/driver_nl80211.c
|
||||
+++ b/src/drivers/driver_nl80211.c
|
||||
@@ -8706,25 +8706,15 @@ static int have_ifidx(struct wpa_driver_
|
||||
@@ -8708,25 +8708,15 @@ static int have_ifidx(struct wpa_driver_
|
||||
|
||||
|
||||
static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
|
||||
|
||||
@@ -568,6 +568,7 @@ static int radius_setup(struct radius_state *s, struct radius_config *c)
|
||||
eap->max_auth_rounds = 100;
|
||||
eap->max_auth_rounds_short = 50;
|
||||
eap->ssl_ctx = tls_init(&conf);
|
||||
eap->pwd_group = 19;
|
||||
if (!eap->ssl_ctx) {
|
||||
wpa_printf(MSG_INFO, "TLS init failed\n");
|
||||
return 1;
|
||||
|
||||
@@ -57,7 +57,7 @@ define Package/wireguard-tools/install
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/src/wg $(1)/usr/bin/
|
||||
$(INSTALL_BIN) ./files/wireguard_watchdog $(1)/usr/bin/
|
||||
$(INSTALL_DIR) $(1)/lib/netifd/proto/
|
||||
$(INSTALL_BIN) ./files/wireguard.sh $(1)/lib/netifd/proto/
|
||||
$(INSTALL_DATA) ./files/wireguard.uc $(1)/lib/netifd/proto/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,wireguard-tools))
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
#!/bin/sh
|
||||
# Copyright 2016-2017 Dan Luedtke <mail@danrl.com>
|
||||
# Licensed to the public under the Apache License 2.0.
|
||||
# shellcheck disable=SC2317
|
||||
|
||||
WG=/usr/bin/wg
|
||||
if [ ! -x $WG ]; then
|
||||
logger -t "wireguard" "error: missing wireguard-tools (${WG})"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
[ -n "$INCLUDE_ONLY" ] || {
|
||||
. /lib/functions.sh
|
||||
. ../netifd-proto.sh
|
||||
init_proto "$@"
|
||||
}
|
||||
|
||||
proto_wireguard_init_config() {
|
||||
renew_handler=1
|
||||
peer_detect=1
|
||||
|
||||
proto_config_add_string "private_key"
|
||||
proto_config_add_int "listen_port"
|
||||
proto_config_add_int "mtu"
|
||||
proto_config_add_string "fwmark"
|
||||
proto_config_add_string "addresses"
|
||||
|
||||
available=1
|
||||
no_proto_task=1
|
||||
}
|
||||
|
||||
ensure_key_is_generated() {
|
||||
local private_key
|
||||
private_key="$(uci get network."$1".private_key)"
|
||||
|
||||
if [ "$private_key" = "generate" ] || [ -z "$private_key" ]; then
|
||||
private_key="$("${WG}" genkey)"
|
||||
uci -q set network."$1".private_key="$private_key" && \
|
||||
uci -q commit network
|
||||
fi
|
||||
}
|
||||
|
||||
proto_wireguard_setup() {
|
||||
local config="$1"
|
||||
|
||||
local private_key listen_port mtu fwmark addresses ip6prefix nohostroute tunlink
|
||||
ensure_key_is_generated "${config}"
|
||||
|
||||
config_load network
|
||||
config_get private_key "${config}" "private_key"
|
||||
config_get listen_port "${config}" "listen_port"
|
||||
config_get addresses "${config}" "addresses"
|
||||
config_get mtu "${config}" "mtu"
|
||||
config_get fwmark "${config}" "fwmark"
|
||||
config_get ip6prefix "${config}" "ip6prefix"
|
||||
config_get nohostroute "${config}" "nohostroute"
|
||||
config_get tunlink "${config}" "tunlink"
|
||||
|
||||
# Add the link only if it didn't already exist
|
||||
ip -br link show "${config}" >/dev/null 2>&1 || ip link add dev "${config}" type wireguard
|
||||
|
||||
[ -n "${mtu}" ] && ip link set mtu "${mtu}" dev "${config}"
|
||||
|
||||
proto_init_update "${config}" 1
|
||||
|
||||
# Build WireGuard configuration entirely in memory
|
||||
local wg_config="[Interface]\n"
|
||||
wg_config="${wg_config}PrivateKey=${private_key}\n"
|
||||
[ -n "${listen_port}" ] && wg_config="${wg_config}ListenPort=${listen_port}\n"
|
||||
[ -n "${fwmark}" ] && wg_config="${wg_config}FwMark=${fwmark}\n"
|
||||
|
||||
# Collect peer configs into wg_config as well
|
||||
local peer_config
|
||||
peer_config=""
|
||||
proto_wireguard_setup_peer_collect() {
|
||||
local section="$1"
|
||||
local peer_block
|
||||
|
||||
config_get_bool route_allowed_ips "$section" "route_allowed_ips" 0
|
||||
config_get_bool disabled "$section" "disabled" 0
|
||||
[ "$disabled" = 1 ] && return;
|
||||
config_get peer_key "$section" "public_key"
|
||||
config_get peer_eph "$section" "endpoint_host"
|
||||
config_get peer_port "$section" "endpoint_port" "51820"
|
||||
config_get peer_a_ips "$section" "allowed_ips"
|
||||
config_get peer_p_ka "$section" "persistent_keepalive"
|
||||
config_get peer_psk "$section" "preshared_key"
|
||||
|
||||
|
||||
[ "${peer_eph##*:}" != "$peer_eph" ] && peer_eph="[$peer_eph]"
|
||||
peer_port=${peer_port:-51820}
|
||||
|
||||
peer_block="\n[Peer]\n"
|
||||
[ -n "${peer_key}" ] && peer_block="${peer_block}PublicKey=${peer_key}\n"
|
||||
[ -n "${peer_psk}" ] && peer_block="${peer_block}PresharedKey=${peer_psk}\n"
|
||||
[ -n "${peer_eph}" ] && peer_block="${peer_block}Endpoint=${peer_eph}${peer_port:+:$peer_port}\n"
|
||||
[ -n "${peer_a_ips}" ] && peer_block="${peer_block}AllowedIPs=${peer_a_ips// /, }\n"
|
||||
[ -n "${peer_p_ka}" ] && peer_block="${peer_block}PersistentKeepalive=${peer_p_ka}\n"
|
||||
|
||||
[ -n "$peer_key" ] && peer_config="$peer_config$peer_block\n"
|
||||
if [ $route_allowed_ips -ne 0 ]; then
|
||||
for allowed_ip in $peer_a_ips; do
|
||||
case "${allowed_ip}" in
|
||||
*:*/*) proto_add_ipv6_route "${allowed_ip%%/*}" "${allowed_ip##*/}" ;;
|
||||
*.*/*) proto_add_ipv4_route "${allowed_ip%%/*}" "${allowed_ip##*/}" ;;
|
||||
*:*) proto_add_ipv6_route "${allowed_ip%%/*}" "128" ;;
|
||||
*.*) proto_add_ipv4_route "${allowed_ip%%/*}" "32" ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
config_foreach proto_wireguard_setup_peer_collect "wireguard_${config}"
|
||||
|
||||
# Combine interface + peer config into one variable
|
||||
wg_config="${wg_config}${peer_config}"
|
||||
|
||||
# Apply configuration directly using wg syncconf via stdin
|
||||
printf "%b" "$wg_config" | ${WG} syncconf "${config}" /dev/stdin
|
||||
local WG_RETURN=$?
|
||||
|
||||
if [ ${WG_RETURN} -ne 0 ]; then
|
||||
echo "Could not sync WireGuard configuration"
|
||||
sleep 5
|
||||
proto_setup_failed "${config}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Assign addresses
|
||||
for address in ${addresses}; do
|
||||
case "${address}" in
|
||||
*:*/*) proto_add_ipv6_address "${address%%/*}" "${address##*/}" ;;
|
||||
*.*/*) proto_add_ipv4_address "${address%%/*}" "${address##*/}" ;;
|
||||
*:*) proto_add_ipv6_address "${address%%/*}" "128" ;;
|
||||
*.*) proto_add_ipv4_address "${address%%/*}" "32" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
for prefix in ${ip6prefix}; do
|
||||
proto_add_ipv6_prefix "$prefix"
|
||||
done
|
||||
|
||||
# Endpoint dependency tracking
|
||||
if [ "${nohostroute}" != "1" ]; then
|
||||
wg show "${config}" endpoints | \
|
||||
sed -E 's/\[?([0-9.:a-f]+)\]?:([0-9]+)/\1 \2/' | \
|
||||
while IFS=$'\t ' read -r key address port; do
|
||||
[ -n "${port}" ] || continue
|
||||
proto_add_host_dependency "${config}" "${address}" "${tunlink}"
|
||||
done
|
||||
fi
|
||||
|
||||
proto_send_update "${config}"
|
||||
}
|
||||
|
||||
proto_wireguard_renew() {
|
||||
local interface="$1"
|
||||
proto_wireguard_setup "$interface"
|
||||
}
|
||||
|
||||
proto_wireguard_teardown() {
|
||||
local config="$1"
|
||||
ip link del dev "${config}" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
[ -n "$INCLUDE_ONLY" ] || {
|
||||
add_protocol wireguard
|
||||
}
|
||||
255
package/network/utils/wireguard-tools/files/wireguard.uc
Normal file
255
package/network/utils/wireguard-tools/files/wireguard.uc
Normal file
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env ucode
|
||||
'use strict';
|
||||
|
||||
import * as fs from 'fs';
|
||||
|
||||
const WG = '/usr/bin/wg';
|
||||
|
||||
function wg_exists() {
|
||||
return fs.access(WG, fs.F_OK);
|
||||
}
|
||||
|
||||
function ensure_key_is_generated(cursor, section_name) {
|
||||
let private_key = cursor.get('network', section_name, 'private_key');
|
||||
|
||||
if (!private_key || private_key == 'generate') {
|
||||
let proc = fs.popen(`${WG} genkey`);
|
||||
if (!proc)
|
||||
return null;
|
||||
|
||||
let generated_key = rtrim(proc.read('all'));
|
||||
proc.close();
|
||||
|
||||
if (generated_key) {
|
||||
cursor.set('network', section_name, 'private_key', generated_key);
|
||||
cursor.commit('network');
|
||||
return generated_key;
|
||||
}
|
||||
}
|
||||
|
||||
return private_key;
|
||||
}
|
||||
|
||||
function parse_address(addr) {
|
||||
if (index(addr, ':') >= 0) {
|
||||
if (index(addr, '/') >= 0) {
|
||||
let parts = split(addr, '/');
|
||||
return { family: 6, address: parts[0], mask: int(parts[1]) };
|
||||
}
|
||||
return { family: 6, address: addr, mask: 128 };
|
||||
}
|
||||
|
||||
if (index(addr, '/') >= 0) {
|
||||
let parts = split(addr, '/');
|
||||
return { family: 4, address: parts[0], mask: int(parts[1]) };
|
||||
}
|
||||
|
||||
return { family: 4, address: addr, mask: 32 };
|
||||
}
|
||||
|
||||
function load_peers(cursor, iface) {
|
||||
let peers = [];
|
||||
let peer_type = sprintf('wireguard_%s', iface);
|
||||
|
||||
cursor.foreach('network', peer_type, (peer_section) => {
|
||||
let disabled = peer_section.disabled;
|
||||
if (disabled == '1')
|
||||
return;
|
||||
|
||||
let route_allowed_ips = peer_section.route_allowed_ips;
|
||||
let peer_key = peer_section.public_key;
|
||||
let peer_eph = peer_section.endpoint_host;
|
||||
let peer_port = peer_section.endpoint_port ?? '51820';
|
||||
let peer_a_ips = peer_section.allowed_ips;
|
||||
let peer_p_ka = peer_section.persistent_keepalive;
|
||||
let peer_psk = peer_section.preshared_key;
|
||||
|
||||
if (!peer_key)
|
||||
return;
|
||||
|
||||
let peer_data = {
|
||||
public_key: peer_key,
|
||||
preshared_key: peer_psk,
|
||||
endpoint_host: peer_eph,
|
||||
endpoint_port: peer_port,
|
||||
allowed_ips: peer_a_ips,
|
||||
persistent_keepalive: peer_p_ka,
|
||||
route_allowed_ips: route_allowed_ips == '1'
|
||||
};
|
||||
|
||||
push(peers, peer_data);
|
||||
});
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
function proto_setup(proto) {
|
||||
if (!wg_exists()) {
|
||||
warn('WireGuard tools not found at ', WG, '\n');
|
||||
proto.setup_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
let iface = proto.iface;
|
||||
let config = proto.config;
|
||||
|
||||
system(sprintf('ip link add dev %s type wireguard 2>/dev/null || true', iface));
|
||||
|
||||
if (config.mtu)
|
||||
system(sprintf('ip link set mtu %d dev %s', int(config.mtu), iface));
|
||||
|
||||
let wg_config = '[Interface]\n';
|
||||
wg_config += sprintf('PrivateKey=%s\n', config.private_key);
|
||||
|
||||
if (config.listen_port)
|
||||
wg_config += sprintf('ListenPort=%d\n', int(config.listen_port));
|
||||
|
||||
if (config.fwmark)
|
||||
wg_config += sprintf('FwMark=%s\n', config.fwmark);
|
||||
|
||||
let ipv4_routes = [];
|
||||
let ipv6_routes = [];
|
||||
|
||||
for (let peer in config.peers) {
|
||||
wg_config += '\n[Peer]\n';
|
||||
wg_config += sprintf('PublicKey=%s\n', peer.public_key);
|
||||
|
||||
if (peer.preshared_key)
|
||||
wg_config += sprintf('PresharedKey=%s\n', peer.preshared_key);
|
||||
|
||||
if (peer.endpoint_host) {
|
||||
let eph = peer.endpoint_host;
|
||||
if (index(eph, ':') >= 0 && eph[0] != '[')
|
||||
eph = sprintf('[%s]', eph);
|
||||
wg_config += sprintf('Endpoint=%s:%s\n', eph, peer.endpoint_port);
|
||||
}
|
||||
|
||||
if (peer.allowed_ips) {
|
||||
let allowed_list = type(peer.allowed_ips) == 'array' ? peer.allowed_ips : split(peer.allowed_ips, ' ');
|
||||
wg_config += sprintf('AllowedIPs=%s\n', join(', ', allowed_list));
|
||||
|
||||
if (peer.route_allowed_ips) {
|
||||
for (let allowed_ip in allowed_list) {
|
||||
let addr_info = parse_address(allowed_ip);
|
||||
let route = { target: addr_info.address, netmask: '' + addr_info.mask };
|
||||
if (addr_info.family == 6)
|
||||
push(ipv6_routes, route);
|
||||
else
|
||||
push(ipv4_routes, route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (peer.persistent_keepalive)
|
||||
wg_config += sprintf('PersistentKeepalive=%s\n', peer.persistent_keepalive);
|
||||
}
|
||||
|
||||
let wg_proc = fs.popen(sprintf('%s syncconf %s /dev/stdin', WG, iface), 'w');
|
||||
if (!wg_proc) {
|
||||
warn('Failed to run wg syncconf for ', iface, '\n');
|
||||
proto.setup_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
wg_proc.write(wg_config);
|
||||
let wg_result = wg_proc.close();
|
||||
|
||||
if (wg_result != 0) {
|
||||
warn('wg syncconf failed for ', iface, '\n');
|
||||
proto.setup_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
system(sprintf('ip link set up dev %s', iface));
|
||||
|
||||
let ipv4_addrs = [];
|
||||
let ipv6_addrs = [];
|
||||
|
||||
if (config.addresses) {
|
||||
let addr_list = split(config.addresses, ' ');
|
||||
for (let address in addr_list) {
|
||||
let addr_info = parse_address(address);
|
||||
let addr = { ipaddr: addr_info.address, mask: '' + addr_info.mask };
|
||||
if (addr_info.family == 6)
|
||||
push(ipv6_addrs, addr);
|
||||
else
|
||||
push(ipv4_addrs, addr);
|
||||
}
|
||||
}
|
||||
|
||||
let link_data = {
|
||||
ifname: iface
|
||||
};
|
||||
|
||||
if (length(ipv4_addrs) > 0)
|
||||
link_data.ipaddr = ipv4_addrs;
|
||||
|
||||
if (length(ipv6_addrs) > 0)
|
||||
link_data.ip6addr = ipv6_addrs;
|
||||
|
||||
if (length(ipv4_routes) > 0)
|
||||
link_data.routes = ipv4_routes;
|
||||
|
||||
if (length(ipv6_routes) > 0)
|
||||
link_data.routes6 = ipv6_routes;
|
||||
|
||||
if (config.ip6prefix) {
|
||||
let prefix_list = split(config.ip6prefix, ' ');
|
||||
if (length(prefix_list) > 0)
|
||||
link_data.ip6prefix = prefix_list;
|
||||
}
|
||||
|
||||
if (config.nohostroute != '1') {
|
||||
let endpoints_proc = fs.popen(sprintf('%s show %s endpoints', WG, iface));
|
||||
if (endpoints_proc) {
|
||||
let endpoints_data = endpoints_proc.read('all');
|
||||
endpoints_proc.close();
|
||||
|
||||
let endpoint_lines = split(endpoints_data, '\n');
|
||||
for (let line in endpoint_lines) {
|
||||
if (!line)
|
||||
continue;
|
||||
|
||||
let parts = split(rtrim(line), '\t');
|
||||
if (length(parts) < 2)
|
||||
continue;
|
||||
|
||||
let endpoint = parts[1];
|
||||
let addr_match = match(endpoint, regexp('\\[?([0-9.:a-f]+)\\]?:([0-9]+)'));
|
||||
if (addr_match && length(addr_match) > 1)
|
||||
proto.add_host_dependency(addr_match[1], config.tunlink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proto.update_link(true, link_data);
|
||||
}
|
||||
|
||||
function proto_teardown(proto) {
|
||||
let iface = proto.iface;
|
||||
system(sprintf('ip link del dev %s 2>/dev/null', iface));
|
||||
proto.update_link(false);
|
||||
}
|
||||
|
||||
function proto_renew(proto) {
|
||||
proto_setup(proto);
|
||||
}
|
||||
|
||||
netifd.add_proto({
|
||||
available: true,
|
||||
no_proto_task: true,
|
||||
'renew-handler': true,
|
||||
name: 'wireguard',
|
||||
|
||||
config: function(ctx) {
|
||||
return {
|
||||
...ctx.data,
|
||||
private_key: ensure_key_is_generated(ctx.uci, ctx.section),
|
||||
peers: load_peers(ctx.uci, ctx.section)
|
||||
};
|
||||
},
|
||||
|
||||
setup: proto_setup,
|
||||
teardown: proto_teardown,
|
||||
renew: proto_renew
|
||||
});
|
||||
Reference in New Issue
Block a user