diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile
index e303e5e570bff446dc9245166faa99d3fff12a32..62fc2cd59d4c2d632483d7fc6babae3b26f99c9c 100644
--- a/contrib/docker/Dockerfile
+++ b/contrib/docker/Dockerfile
@@ -9,7 +9,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
     file \
     git \
     python3 \
+    python3-dev \
     python3-distutils \
+    python3-pyelftools \
+    python3-setuptools \
     build-essential \
     gawk \
     unzip \
@@ -26,6 +29,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
     shellcheck \
     libnss-unknown \
     openssh-client \
+    swig \
     && apt-get clean \
     && rm -rf /var/lib/apt/lists/*
 
diff --git a/docs/user/supported_devices.rst b/docs/user/supported_devices.rst
index 6808c0e0f212859e12318b80fb268bb87cd84f30..78718ec775bf0c8e0356622a88cd50cf0ec1cf82 100644
--- a/docs/user/supported_devices.rst
+++ b/docs/user/supported_devices.rst
@@ -339,10 +339,6 @@ mediatek-mt7622
 
   - UniFi 6 LR (v1)
 
-* Xiaomi
-
-  - AX3200 (RB03)
-
 mpc85xx-p1010
 -------------
 
@@ -461,8 +457,6 @@ ramips-mt7621
 
 * Ubiquiti
 
-  - EdgeRouter X
-  - EdgeRouter X-SFP
   - UniFi 6 Lite
   - UniFi nanoHD
 
diff --git a/modules b/modules
index 857943ff2a768081ff00e014ff04e00b6a12c13b..0fa4d2ea447fc85c19cdbcc828fc83beb2f00ecc 100644
--- a/modules
+++ b/modules
@@ -1,16 +1,16 @@
 GLUON_FEEDS='gluon packages routing'
 
 OPENWRT_REPO=https://github.com/openwrt/openwrt.git
-OPENWRT_BRANCH=openwrt-23.05
-OPENWRT_COMMIT=cd9998ef1b7b842bf052fc5e7034f23a22e67bf7
+OPENWRT_BRANCH=openwrt-24.10
+OPENWRT_COMMIT=45f9f1551223b516cccbcca1e252e722bb3dbc08
 
 PACKAGES_GLUON_REPO=https://github.com/freifunk-gluon/packages.git
 PACKAGES_GLUON_COMMIT=3d08b0fee8dc5d96d8bcdb985fad1d5564de4022
 
 PACKAGES_PACKAGES_REPO=https://github.com/openwrt/packages.git
-PACKAGES_PACKAGES_BRANCH=openwrt-23.05
-PACKAGES_PACKAGES_COMMIT=7ee4fe7705f8b44ccfd2ec9470e91c8d900a65ab
+PACKAGES_PACKAGES_BRANCH=openwrt-24.10
+PACKAGES_PACKAGES_COMMIT=d364dcb7e55ac4202853c48e2788f0d4fbb5c513
 
 PACKAGES_ROUTING_REPO=https://github.com/openwrt/routing.git
-PACKAGES_ROUTING_BRANCH=openwrt-23.05
-PACKAGES_ROUTING_COMMIT=67fb1bc0cbe1cf26748e83acb872513434bd0471
+PACKAGES_ROUTING_BRANCH=openwrt-24.10
+PACKAGES_ROUTING_COMMIT=3f15699240c076d5ee9ed697fa5ef45355423f6f
diff --git a/package/gluon-mesh-batman-adv/files/lib/netifd/proto/gluon_bat0.sh b/package/gluon-mesh-batman-adv/files/lib/netifd/proto/gluon_bat0.sh
index 91ea0d356794a1dfec4f5e2ffd2eb253dc15a6f8..8424050f6446ff81f30771babed50f8dc72fc5ac 100755
--- a/package/gluon-mesh-batman-adv/files/lib/netifd/proto/gluon_bat0.sh
+++ b/package/gluon-mesh-batman-adv/files/lib/netifd/proto/gluon_bat0.sh
@@ -47,7 +47,6 @@ proto_gluon_bat0_setup() {
 
 	batctl orig_interval 5000
 	batctl hop_penalty "$(lookup_uci 'gluon.mesh_batman_adv.hop_penalty' 15)"
-	batctl noflood_mark 0x4/0x4
 
 	case "$gw_mode" in
 		server)
diff --git a/patches/openwrt/0003-dropbear-add-a-failsafe-mode-that-will-always-allow-password-less-root-login.patch b/patches/openwrt/0003-dropbear-add-a-failsafe-mode-that-will-always-allow-password-less-root-login.patch
index d2010181b133fbe88792fd3634e90f5431428761..acdf4630d318772506fbda45043a6c18ef5b802f 100644
--- a/patches/openwrt/0003-dropbear-add-a-failsafe-mode-that-will-always-allow-password-less-root-login.patch
+++ b/patches/openwrt/0003-dropbear-add-a-failsafe-mode-that-will-always-allow-password-less-root-login.patch
@@ -6,13 +6,13 @@ Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
 
 diff --git a/package/network/services/dropbear/patches/700-failsafe-mode.patch b/package/network/services/dropbear/patches/700-failsafe-mode.patch
 new file mode 100644
-index 0000000000000000000000000000000000000000..bd9cf1ce4234d94bcc5b782cc753f59993f999bc
+index 0000000000000000000000000000000000000000..9b619ce80b2963c67ac62bcf95b33b28b505ad2a
 --- /dev/null
 +++ b/package/network/services/dropbear/patches/700-failsafe-mode.patch
 @@ -0,0 +1,57 @@
-+--- a/svr-auth.c
-++++ b/svr-auth.c
-+@@ -125,10 +125,11 @@ void recv_msg_userauth_request() {
++--- a/src/svr-auth.c
+++++ b/src/svr-auth.c
++@@ -124,10 +124,11 @@ void recv_msg_userauth_request() {
 + 				AUTH_METHOD_NONE_LEN) == 0) {
 + 		TRACE(("recv_msg_userauth_request: 'none' request"))
 + 		if (valid_user
@@ -28,38 +28,38 @@ index 0000000000000000000000000000000000000000..bd9cf1ce4234d94bcc5b782cc753f599
 + 		{
 + 			dropbear_log(LOG_NOTICE, 
 + 					"Auth succeeded with blank password for '%s' from %s",
-+--- a/svr-runopts.c
-++++ b/svr-runopts.c
-+@@ -77,6 +77,7 @@ static void printhelp(const char * progn
++--- a/src/svr-runopts.c
+++++ b/src/svr-runopts.c
++@@ -82,6 +82,7 @@ static void printhelp(const char * progn
 + 					"-s		Disable password logins\n"
 + 					"-g		Disable password logins for root\n"
 + 					"-B		Allow blank password logins\n"
-++					"-f		Failsafe mode: always allow password-less root login\n"
+++					"-f             Failsafe mode: always allow password-less root login\n"
++ 					"-t		Enable two-factor authentication (both password and public key required)\n"
 + #endif
 + 					"-T		Maximum authentication tries (default %d)\n"
-+ #if DROPBEAR_SVR_LOCALTCPFWD
-+@@ -144,6 +145,7 @@ void svr_getopts(int argc, char ** argv)
++@@ -166,6 +167,7 @@ void svr_getopts(int argc, char ** argv)
 + 	svr_opts.noauthpass = 0;
 + 	svr_opts.norootpass = 0;
 + 	svr_opts.allowblankpass = 0;
 ++	svr_opts.failsafe_mode = 0;
++ 	svr_opts.multiauthmethod = 0;
 + 	svr_opts.maxauthtries = MAX_AUTH_TRIES;
 + 	svr_opts.inetdmode = 0;
-+ 	svr_opts.portcount = 0;
-+@@ -266,6 +268,9 @@ void svr_getopts(int argc, char ** argv)
-+ 				case 'B':
-+ 					svr_opts.allowblankpass = 1;
++@@ -263,6 +265,9 @@ void svr_getopts(int argc, char ** argv)
++ 				case '2':
++ 					next = &reexec_fd_arg;
 + 					break;
 ++				case 'f':
 ++					svr_opts.failsafe_mode = 1;
 ++					break;
 + #endif
-+ 				case 'h':
-+ 					printhelp(argv[0]);
-+--- a/runopts.h
-++++ b/runopts.h
-+@@ -106,6 +106,8 @@ typedef struct svr_runopts {
-+ 	int allowblankpass;
++ 				case 'p':
++ 					nextisport = 1;
++--- a/src/runopts.h
+++++ b/src/runopts.h
++@@ -110,6 +110,8 @@ typedef struct svr_runopts {
++ 	int multiauthmethod;
 + 	unsigned int maxauthtries;
 + 
 ++	int failsafe_mode;
diff --git a/patches/openwrt/0009-mt76-include-fixes-for-MT7603-MT7612.patch b/patches/openwrt/0004-mt76-include-fixes-for-MT7603-MT7612.patch
similarity index 100%
rename from patches/openwrt/0009-mt76-include-fixes-for-MT7603-MT7612.patch
rename to patches/openwrt/0004-mt76-include-fixes-for-MT7603-MT7612.patch
diff --git a/patches/openwrt/0004-xrx200-migrate-fritz7360-v2-using-incorrect-image.patch b/patches/openwrt/0004-xrx200-migrate-fritz7360-v2-using-incorrect-image.patch
deleted file mode 100644
index bcf694ab13a0706c928745956aa73103c4d24c8b..0000000000000000000000000000000000000000
--- a/patches/openwrt/0004-xrx200-migrate-fritz7360-v2-using-incorrect-image.patch
+++ /dev/null
@@ -1,81 +0,0 @@
-From: Grische <github@grische.xyz>
-Date: Sun, 18 Sep 2022 14:03:16 +0200
-Subject: xrx200: migrate fritz7360-v2 using incorrect image
-
-Migrate AVM FRITZ!Box 7360 v2 boards flashed with the incorrect v1 image to use
-the newly added v2 target image during the next upgrade.
-Using the v2 target image allows the boards to read the TFFS partition, which
-is misaligned when using the v1 image.
-
-Ref: https://github.com/freifunk-gluon/gluon/pull/2648
-
-Co-authored-by: Jan-Niklas Burfeind <git@aiyionpri.me>
-
-diff --git a/target/linux/lantiq/xrx200/base-files/lib/preinit/01_sysinfo.sh b/target/linux/lantiq/xrx200/base-files/lib/preinit/01_sysinfo.sh
-new file mode 100644
-index 0000000000000000000000000000000000000000..fab50d708e872f819c643cea79327e4f438de524
---- /dev/null
-+++ b/target/linux/lantiq/xrx200/base-files/lib/preinit/01_sysinfo.sh
-@@ -0,0 +1,62 @@
-+set_sysinfo_xrx200_for_fritz7360_model() {
-+    local board_name=$1
-+    local model
-+    local urlader_version urlader_memsize urlader_flashsize
-+    local hexdump_format='4/1 "%02x""\n"'
-+
-+    # Values are based on urlader-parser-py
-+    # https://github.com/grische/urlader-parser-py/blob/42970bf8dec7962317df4ff734c57ebf36df8905/parser.py#L77-L84
-+    urlader_version="$(dd if=/dev/mtd0ro bs=1 skip=$((0x580+0x0)) count=4 | hexdump -e "${hexdump_format}")"
-+    if [ "${urlader_version}" != "00000003" ]; then
-+        logger -s -p warn -t sysinfo-xrx200 "unexpected urlader version found: ${urlader_version}"
-+        return
-+    fi
-+
-+    urlader_memsize="$(dd if=/dev/mtd0ro bs=1 skip=$((0x580+0x4)) count=4 | hexdump -e "${hexdump_format}")"
-+    if [ "${urlader_memsize}" != "08000000" ]; then
-+        logger -s -p warn -t sysinfo-xrx200 "unexpected memsize found: ${urlader_memsize}"
-+        return
-+    fi
-+
-+    urlader_flashsize="$(dd if=/dev/mtd0ro bs=1 skip=$((0x580+0x8)) count=4 | hexdump -e "${hexdump_format}")"
-+    case "${urlader_flashsize}" in
-+        "02000000")    # 32MB
-+            # see vr9_avm_fritz7360-v2.dts
-+            board_name="avm,fritz7360-v2"
-+            model="AVM FRITZ!Box 7360 V2"
-+            ;;
-+        "01000000")    # 16MB
-+            return
-+            ;;
-+        *)
-+            logger -s -p warn -t sysinfo-xrx200 "unexpected flashsize found: ${urlader_flashsize}"
-+            return
-+            ;;
-+    esac
-+
-+    logger -s -p notice -t sysinfo-xrx200 "detected ${board_name} from urlader partition /dev/mtd0ro. Enforcing model ${model}."
-+    echo "${board_name}" > /tmp/sysinfo/board_name
-+    echo "${model}" > /tmp/sysinfo/model
-+}
-+
-+do_sysinfo_xrx200() {
-+    local reported_board board_name model
-+
-+    [ -d /proc/device-tree ] || return
-+    reported_board="$(strings /proc/device-tree/compatible | head -1)"
-+
-+    mkdir -p /tmp/sysinfo
-+    # 7360 is notoriously known for not writing "v2" on their labels and many
-+    # routers have flashed the wrong firmware with the wrong flash layout.
-+    # We ensure that the underlying hardware is reported correctly, so that
-+    # future upgrades will use the correct flash layout.
-+    # Using 7360v2 hardware, an upgrade from a 7360v1/sl firmware to a 7360v2
-+    # is working.
-+    case "${reported_board}" in
-+        avm,fritz7360sl)
-+            set_sysinfo_xrx200_for_fritz7360_model "${reported_board}"
-+            ;;
-+    esac
-+}
-+
-+boot_hook_add preinit_main do_sysinfo_xrx200
diff --git a/patches/openwrt/0005-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch b/patches/openwrt/0005-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch
deleted file mode 100644
index a38ea3ad2aabb91bc66f7ae7ba6fbfc7bcecef6a..0000000000000000000000000000000000000000
--- a/patches/openwrt/0005-kernel-bridge-Implement-MLD-Querier-wake-up-calls-Android-bug-workaround.patch
+++ /dev/null
@@ -1,856 +0,0 @@
-From: Linus Lüssing <linus.luessing@c0d3.blue>
-Date: Sat, 1 Jan 2022 10:09:13 +0100
-Subject: kernel: bridge: Implement MLD Querier wake-up calls / Android bug workaround
-
-Implement a configurable MLD Querier wake-up calls "feature" which
-works around a widely spread Android bug in connection with IGMP/MLD
-snooping.
-
-Currently there are mobile devices (e.g. Android) which are not able
-to receive and respond to MLD Queries reliably because the Wifi driver
-filters a lot of ICMPv6 when the device is asleep - including
-MLD. This in turn breaks IPv6 communication when MLD Snooping is
-enabled. However there is one ICMPv6 type which is allowed to pass and
-which can be used to wake up the mobile device: ICMPv6 Echo Requests.
-
-If this bridge is the selected MLD Querier then setting
-"multicast_wakeupcall" to a number n greater than 0 will send n
-ICMPv6 Echo Requests to each host behind this port to wake
-them up with each MLD Query. Upon receiving a matching ICMPv6 Echo
-Reply an MLD Query with a unicast ethernet destination will be sent
-to the specific host(s).
-
-Link: https://issuetracker.google.com/issues/149630944
-Link: https://github.com/freifunk-gluon/gluon/issues/1832
-
-Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
-
-diff --git a/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch b/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch
-new file mode 100644
-index 0000000000000000000000000000000000000000..077a563b6066cd1d3aee4b1e82328e8cc5e042ea
---- /dev/null
-+++ b/package/network/config/netifd/patches/0001-bridge-Add-multicast_wakeupcall-option.patch
-@@ -0,0 +1,142 @@
-+From d23a49e6542dc068b12fbc7b6a4520f9fb3626f9 Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue>
-+Date: Sun, 5 Jul 2020 23:33:51 +0200
-+Subject: [PATCH] bridge: Add multicast_wakeupcall option
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+This makes the new per bridge port multicast_wakeupcall feature
-+for the Linux bridge configurable for wireless interfaces and enables it
-+by default for an AP interface.
-+
-+The MLD Querier wake-up calls "feature" works around a widely spread Android
-+bug in connection with IGMP/MLD snooping.
-+
-+Currently there are mobile devices (e.g. Android) which are not able
-+to receive and respond to MLD Queries reliably because the Wifi driver
-+filters a lot of ICMPv6 when the device is asleep - including
-+MLD. This in turn breaks IPv6 communication when MLD Snooping is
-+enabled. However there is one ICMPv6 type which is allowed to pass and
-+which can be used to wake up the mobile device: ICMPv6 Echo Requests.
-+
-+If this bridge is the selected MLD Querier then setting
-+"multicast_wakeupcall" to a number n greater than 0 will send n
-+ICMPv6 Echo Requests to each host behind this port to wake
-+them up with each MLD Query. Upon receiving a matching ICMPv6 Echo
-+Reply an MLD Query with a unicast ethernet destination will be sent
-+to the specific host(s).
-+
-+Link: https://issuetracker.google.com/issues/149630944
-+Link: https://github.com/freifunk-gluon/gluon/issues/1832
-+
-+Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
-+---
-+ device.c       |  9 +++++++++
-+ device.h       |  3 +++
-+ system-linux.c | 13 +++++++++++++
-+ 3 files changed, 25 insertions(+)
-+
-+--- a/device.c
-++++ b/device.c
-+@@ -49,6 +49,7 @@ static const struct blobmsg_policy dev_a
-+ 	[DEV_ATTR_NEIGHGCSTALETIME] = { .name = "neighgcstaletime", .type = BLOBMSG_TYPE_INT32 },
-+ 	[DEV_ATTR_DADTRANSMITS] = { .name = "dadtransmits", .type = BLOBMSG_TYPE_INT32 },
-+ 	[DEV_ATTR_MULTICAST_TO_UNICAST] = { .name = "multicast_to_unicast", .type = BLOBMSG_TYPE_BOOL },
-++	[DEV_ATTR_MULTICAST_WAKEUPCALL] = { .name = "multicast_wakeupcall", .type = BLOBMSG_TYPE_INT32 },
-+ 	[DEV_ATTR_MULTICAST_ROUTER] = { .name = "multicast_router", .type = BLOBMSG_TYPE_INT32 },
-+ 	[DEV_ATTR_MULTICAST_FAST_LEAVE] = { .name = "multicast_fast_leave", . type = BLOBMSG_TYPE_BOOL },
-+ 	[DEV_ATTR_MULTICAST] = { .name ="multicast", .type = BLOBMSG_TYPE_BOOL },
-+@@ -275,6 +276,7 @@ device_merge_settings(struct device *dev
-+ 	n->multicast = s->flags & DEV_OPT_MULTICAST ?
-+ 		s->multicast : os->multicast;
-+ 	n->multicast_to_unicast = s->multicast_to_unicast;
-++	n->multicast_wakeupcall = s->multicast_wakeupcall;
-+ 	n->multicast_router = s->multicast_router;
-+ 	n->multicast_fast_leave = s->multicast_fast_leave;
-+ 	n->learning = s->learning;
-+@@ -449,6 +451,11 @@ device_init_settings(struct device *dev,
-+ 		s->flags |= DEV_OPT_MULTICAST_TO_UNICAST;
-+ 	}
-+ 
-++	if ((cur = tb[DEV_ATTR_MULTICAST_WAKEUPCALL])) {
-++		s->multicast_wakeupcall = blobmsg_get_u32(cur);
-++		s->flags |= DEV_OPT_MULTICAST_WAKEUPCALL;
-++	}
-++
-+ 	if ((cur = tb[DEV_ATTR_MULTICAST_ROUTER])) {
-+ 		s->multicast_router = blobmsg_get_u32(cur);
-+ 		if (s->multicast_router <= 2)
-+@@ -1372,6 +1379,8 @@ device_dump_status(struct blob_buf *b, s
-+ 			blobmsg_add_u32(b, "dadtransmits", st.dadtransmits);
-+ 		if (st.flags & DEV_OPT_MULTICAST_TO_UNICAST)
-+ 			blobmsg_add_u8(b, "multicast_to_unicast", st.multicast_to_unicast);
-++		if (st.flags & DEV_OPT_MULTICAST_WAKEUPCALL)
-++			blobmsg_add_u32(b, "multicast_wakeupcall", st.multicast_wakeupcall);
-+ 		if (st.flags & DEV_OPT_MULTICAST_ROUTER)
-+ 			blobmsg_add_u32(b, "multicast_router", st.multicast_router);
-+ 		if (st.flags & DEV_OPT_MULTICAST_FAST_LEAVE)
-+--- a/device.h
-++++ b/device.h
-+@@ -44,6 +44,7 @@ enum {
-+ 	DEV_ATTR_NEIGHREACHABLETIME,
-+ 	DEV_ATTR_DADTRANSMITS,
-+ 	DEV_ATTR_MULTICAST_TO_UNICAST,
-++	DEV_ATTR_MULTICAST_WAKEUPCALL,
-+ 	DEV_ATTR_MULTICAST_ROUTER,
-+ 	DEV_ATTR_MULTICAST_FAST_LEAVE,
-+ 	DEV_ATTR_MULTICAST,
-+@@ -144,6 +145,7 @@ enum {
-+ 	DEV_OPT_GRO			= (1ULL << 37),
-+ 	DEV_OPT_MASTER			= (1ULL << 38),
-+ 	DEV_OPT_EEE			= (1ULL << 39),
-++	DEV_OPT_MULTICAST_WAKEUPCALL	= (1ULL << 63),
-+ };
-+ 
-+ /* events broadcasted to all users of a device */
-+@@ -205,6 +207,7 @@ struct device_settings {
-+ 	int neigh4locktime;
-+ 	unsigned int dadtransmits;
-+ 	bool multicast_to_unicast;
-++	unsigned int multicast_wakeupcall;
-+ 	unsigned int multicast_router;
-+ 	bool multicast_fast_leave;
-+ 	bool multicast;
-+--- a/system-linux.c
-++++ b/system-linux.c
-+@@ -536,6 +536,11 @@ static void system_bridge_set_multicast_
-+ 	system_set_dev_sysfs("brport/multicast_to_unicast", dev->ifname, val);
-+ }
-+ 
-++static void system_bridge_set_multicast_wakeupcall(struct device *dev, const char *val)
-++{
-++	system_set_dev_sysfs("brport/multicast_wakeupcall", dev->ifname, val);
-++}
-++
-+ static void system_bridge_set_multicast_fast_leave(struct device *dev, const char *val)
-+ {
-+ 	system_set_dev_sysfs("brport/multicast_fast_leave", dev->ifname, val);
-+@@ -923,8 +928,10 @@ static char *system_get_bridge(const cha
-+ static void
-+ system_bridge_set_wireless(struct device *bridge, struct device *dev)
-+ {
-++	unsigned int mcast_wakeupcall = dev->wireless_ap ? 2 : 0;
-+ 	bool mcast_to_ucast = dev->wireless_ap;
-+ 	bool hairpin;
-++	char buf[64];
-+ 
-+ 	if (dev->settings.flags & DEV_OPT_MULTICAST_TO_UNICAST)
-+ 		mcast_to_ucast = dev->settings.multicast_to_unicast;
-+@@ -939,6 +946,12 @@ system_bridge_set_wireless(struct device
-+ 	system_bridge_set_multicast_to_unicast(dev, mcast_to_ucast ? "1" : "0");
-+ 	system_bridge_set_hairpin_mode(dev, hairpin ? "1" : "0");
-+ 	system_bridge_set_proxyarp_wifi(dev, dev->wireless_proxyarp ? "1" : "0");
-++
-++	if (bridge->settings.flags & DEV_OPT_MULTICAST_WAKEUPCALL)
-++		mcast_wakeupcall = dev->settings.multicast_wakeupcall;
-++
-++	snprintf(buf, sizeof(buf), "%u", mcast_wakeupcall);
-++	system_bridge_set_multicast_wakeupcall(dev, buf);
-+ }
-+ 
-+ int system_bridge_addif(struct device *bridge, struct device *dev)
-diff --git a/target/linux/generic/config-5.15 b/target/linux/generic/config-5.15
-index fd386467ef4585a5cb10c887a8cb922b7a2d9197..e18b099011c2e407f701d8afa5c37bb208b5a6ce 100644
---- a/target/linux/generic/config-5.15
-+++ b/target/linux/generic/config-5.15
-@@ -762,6 +762,7 @@ CONFIG_BRIDGE=y
- # CONFIG_BRIDGE_EBT_T_NAT is not set
- # CONFIG_BRIDGE_EBT_VLAN is not set
- CONFIG_BRIDGE_IGMP_SNOOPING=y
-+CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS=y
- # CONFIG_BRIDGE_MRP is not set
- # CONFIG_BRIDGE_NETFILTER is not set
- # CONFIG_BRIDGE_NF_EBTABLES is not set
-diff --git a/target/linux/generic/hack-5.15/602-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch b/target/linux/generic/hack-5.15/602-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch
-new file mode 100644
-index 0000000000000000000000000000000000000000..91dd13e51549f40aa11a01d53e11be1a70f25d86
---- /dev/null
-+++ b/target/linux/generic/hack-5.15/602-bridge-Implement-MLD-Querier-wake-up-calls-Android-b.patch
-@@ -0,0 +1,663 @@
-+From 4529dcf18d4c5e05d30cd2d6fabfbae201e6c347 Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue>
-+Date: Mon, 29 Jun 2020 19:04:05 +0200
-+Subject: [PATCH] bridge: Implement MLD Querier wake-up calls / Android bug
-+ workaround
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+Implement a configurable MLD Querier wake-up calls "feature" which
-+works around a widely spread Android bug in connection with IGMP/MLD
-+snooping.
-+
-+Currently there are mobile devices (e.g. Android) which are not able
-+to receive and respond to MLD Queries reliably because the Wifi driver
-+filters a lot of ICMPv6 when the device is asleep - including
-+MLD. This in turn breaks IPv6 communication when MLD Snooping is
-+enabled. However there is one ICMPv6 type which is allowed to pass and
-+which can be used to wake up the mobile device: ICMPv6 Echo Requests.
-+
-+If this bridge is the selected MLD Querier then setting
-+"multicast_wakeupcall" to a number n greater than 0 will send n
-+ICMPv6 Echo Requests to each host behind this port to wake
-+them up with each MLD Query. Upon receiving a matching ICMPv6 Echo
-+Reply an MLD Query with a unicast ethernet destination will be sent
-+to the specific host(s).
-+
-+Link: https://issuetracker.google.com/issues/149630944
-+Link: https://github.com/freifunk-gluon/gluon/issues/1832
-+
-+Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
-+---
-+ include/linux/if_bridge.h    |   1 +
-+ include/net/addrconf.h       |   1 +
-+ include/uapi/linux/if_link.h |   1 +
-+ net/bridge/Kconfig           |  26 ++++
-+ net/bridge/br_fdb.c          |  10 ++
-+ net/bridge/br_input.c        |   4 +-
-+ net/bridge/br_multicast.c    | 291 ++++++++++++++++++++++++++++++++++-
-+ net/bridge/br_netlink.c      |  19 +++
-+ net/bridge/br_private.h      |  20 +++
-+ net/bridge/br_sysfs_if.c     |  18 +++
-+ net/core/rtnetlink.c         |   2 +-
-+ net/ipv6/mcast_snoop.c       |   3 +-
-+ 12 files changed, 386 insertions(+), 10 deletions(-)
-+
-+--- a/include/linux/if_bridge.h
-++++ b/include/linux/if_bridge.h
-+@@ -59,6 +59,7 @@ struct br_ip_list {
-+ #define BR_MRP_LOST_IN_CONT	BIT(19)
-+ #define BR_TX_FWD_OFFLOAD	BIT(20)
-+ #define BR_BPDU_FILTER		BIT(21)
-++#define BR_MULTICAST_WAKEUPCALL	BIT(22)
-+ 
-+ #define BR_DEFAULT_AGEING_TIME	(300 * HZ)
-+ 
-+--- a/include/net/addrconf.h
-++++ b/include/net/addrconf.h
-+@@ -241,6 +241,7 @@ void ipv6_mc_unmap(struct inet6_dev *ide
-+ void ipv6_mc_remap(struct inet6_dev *idev);
-+ void ipv6_mc_init_dev(struct inet6_dev *idev);
-+ void ipv6_mc_destroy_dev(struct inet6_dev *idev);
-++int ipv6_mc_check_icmpv6(struct sk_buff *skb);
-+ int ipv6_mc_check_mld(struct sk_buff *skb);
-+ void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp);
-+ 
-+--- a/include/uapi/linux/if_link.h
-++++ b/include/uapi/linux/if_link.h
-+@@ -537,6 +537,7 @@ enum {
-+ 	IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT,
-+ 	IFLA_BRPORT_MCAST_EHT_HOSTS_CNT,
-+ 	IFLA_BRPORT_BPDU_FILTER,
-++	IFLA_BRPORT_MCAST_WAKEUPCALL,
-+ 	__IFLA_BRPORT_MAX
-+ };
-+ #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
-+--- a/net/bridge/Kconfig
-++++ b/net/bridge/Kconfig
-+@@ -48,6 +48,32 @@ config BRIDGE_IGMP_SNOOPING
-+ 
-+ 	  If unsure, say Y.
-+ 
-++config BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++	bool "MLD Querier wake-up calls"
-++	depends on BRIDGE_IGMP_SNOOPING
-++	depends on IPV6
-++	help
-++	  If you say Y here, then the MLD Snooping Querier will be built
-++	  with a per bridge port wake-up call "feature"/workaround.
-++
-++	  Currently there are mobile devices (e.g. Android) which are not able
-++	  to receive and respond to MLD Queries reliably because the Wifi driver
-++	  filters a lot of ICMPv6 when the device is asleep - including MLD.
-++	  This in turn breaks IPv6 communication when MLD Snooping is enabled.
-++	  However there is one ICMPv6 type which is allowed to pass and
-++	  which can be used to wake up the mobile device: ICMPv6 Echo Requests.
-++
-++	  If this bridge is the selected MLD Querier then setting
-++	  "multicast_wakeupcall" to a number n greater than 0 will send n
-++	  ICMPv6 Echo Requests to each host behind this port to wake them up
-++	  with each MLD Query. Upon receiving a matching ICMPv6 Echo Reply
-++	  an MLD Query with a unicast ethernet destination will be sent to the
-++	  specific host(s).
-++
-++	  Say N to exclude this support and reduce the binary size.
-++
-++	  If unsure, say N.
-++
-+ config BRIDGE_VLAN_FILTERING
-+ 	bool "VLAN filtering"
-+ 	depends on BRIDGE
-+--- a/net/bridge/br_fdb.c
-++++ b/net/bridge/br_fdb.c
-+@@ -84,6 +84,10 @@ static void fdb_rcu_free(struct rcu_head
-+ {
-+ 	struct net_bridge_fdb_entry *ent
-+ 		= container_of(head, struct net_bridge_fdb_entry, rcu);
-++
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++	del_timer_sync(&ent->wakeupcall_timer);
-++#endif
-+ 	kmem_cache_free(br_fdb_cache, ent);
-+ }
-+ 
-+@@ -518,6 +522,12 @@ static struct net_bridge_fdb_entry *fdb_
-+ 		fdb->key.vlan_id = vid;
-+ 		fdb->flags = flags;
-+ 		fdb->updated = fdb->used = jiffies;
-++
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++		timer_setup(&fdb->wakeupcall_timer,
-++			    br_multicast_send_wakeupcall, 0);
-++#endif
-++
-+ 		if (rhashtable_lookup_insert_fast(&br->fdb_hash_tbl,
-+ 						  &fdb->rhnode,
-+ 						  br_fdb_rht_params)) {
-+--- a/net/bridge/br_input.c
-++++ b/net/bridge/br_input.c
-+@@ -169,8 +169,10 @@ int br_handle_frame_finish(struct net *n
-+ 	if (dst) {
-+ 		unsigned long now = jiffies;
-+ 
-+-		if (test_bit(BR_FDB_LOCAL, &dst->flags))
-++		if (test_bit(BR_FDB_LOCAL, &dst->flags)) {
-++			br_multicast_wakeupcall_rcv(brmctx, pmctx, skb, vid);
-+ 			return br_pass_frame_up(skb, false);
-++		}
-+ 
-+ 		if (now != dst->used)
-+ 			dst->used = now;
-+--- a/net/bridge/br_multicast.c
-++++ b/net/bridge/br_multicast.c
-+@@ -950,15 +950,16 @@ static struct sk_buff *br_ip6_multicast_
-+ 						    const struct in6_addr *group,
-+ 						    bool with_srcs, bool over_llqt,
-+ 						    u8 sflag, u8 *igmp_type,
-+-						    bool *need_rexmit)
-++						    bool *need_rexmit,
-++						    bool delay)
-+ {
-+ 	struct net_bridge_port *p = pg ? pg->key.port : NULL;
-+ 	struct net_bridge_group_src *ent;
-+ 	size_t pkt_size, mld_hdr_size;
-+ 	unsigned long now = jiffies;
-++	unsigned long interval = 0;
-+ 	struct mld2_query *mld2q;
-+ 	void *csum_start = NULL;
-+-	unsigned long interval;
-+ 	__sum16 *csum = NULL;
-+ 	struct ipv6hdr *ip6h;
-+ 	struct mld_msg *mldq;
-+@@ -1040,9 +1041,13 @@ static struct sk_buff *br_ip6_multicast_
-+ 
-+ 	/* ICMPv6 */
-+ 	skb_set_transport_header(skb, skb->len);
-+-	interval = ipv6_addr_any(group) ?
-+-			brmctx->multicast_query_response_interval :
-+-			brmctx->multicast_last_member_interval;
-++	if (delay) {
-++		interval = ipv6_addr_any(group) ?
-++				brmctx->multicast_query_response_interval :
-++				brmctx->multicast_last_member_interval;
-++		interval = jiffies_to_msecs(interval);
-++	}
-++
-+ 	*igmp_type = ICMPV6_MGM_QUERY;
-+ 	switch (brmctx->multicast_mld_version) {
-+ 	case 1:
-+@@ -1050,7 +1055,7 @@ static struct sk_buff *br_ip6_multicast_
-+ 		mldq->mld_type = ICMPV6_MGM_QUERY;
-+ 		mldq->mld_code = 0;
-+ 		mldq->mld_cksum = 0;
-+-		mldq->mld_maxdelay = htons((u16)jiffies_to_msecs(interval));
-++		mldq->mld_maxdelay = htons((u16)interval);
-+ 		mldq->mld_reserved = 0;
-+ 		mldq->mld_mca = *group;
-+ 		csum = &mldq->mld_cksum;
-+@@ -1141,7 +1146,7 @@ static struct sk_buff *br_multicast_allo
-+ 						    &ip6_dst, &group->dst.ip6,
-+ 						    with_srcs, over_lmqt,
-+ 						    sflag, igmp_type,
-+-						    need_rexmit);
-++						    need_rexmit, true);
-+ 	}
-+ #endif
-+ 	}
-+@@ -1623,6 +1628,169 @@ static void br_multicast_select_own_quer
-+ #endif
-+ }
-+ 
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++
-++#define BR_MC_WAKEUP_ID htons(0xEC6B) /* random identifier */
-++#define BR_MC_ETH_ZERO { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
-++#define BR_MC_IN6_ZERO \
-++{ \
-++	.s6_addr32[0] = 0, .s6_addr32[1] = 0, \
-++	.s6_addr32[2] = 0, .s6_addr32[3] = 0, \
-++}
-++
-++#define BR_MC_IN6_FE80 \
-++{ \
-++	.s6_addr32[0] = htonl(0xfe800000), \
-++	.s6_addr32[1] = 0, \
-++	.s6_addr32[2] = htonl(0x000000ff), \
-++	.s6_addr32[3] = htonl(0xfe000000), \
-++}
-++
-++#define BR_MC_ECHO_LEN sizeof(pkt->echohdr)
-++
-++static struct sk_buff *br_multicast_alloc_wakeupcall(struct net_bridge *br,
-++						     struct net_bridge_port *port,
-++						     u8 *eth_dst)
-++{
-++	struct in6_addr ip6_src, ip6_dst = BR_MC_IN6_FE80;
-++	struct sk_buff *skb;
-++	__wsum csum_part;
-++	__sum16 csum;
-++
-++	struct wakeupcall_pkt {
-++		struct ethhdr ethhdr;
-++		struct ipv6hdr ip6hdr;
-++		struct icmp6hdr echohdr;
-++	} __packed;
-++
-++	struct wakeupcall_pkt *pkt;
-++
-++	static const struct wakeupcall_pkt __pkt_template = {
-++		.ethhdr = {
-++			.h_dest = BR_MC_ETH_ZERO, // update
-++			.h_source = BR_MC_ETH_ZERO, // update
-++			.h_proto = htons(ETH_P_IPV6),
-++		},
-++		.ip6hdr = {
-++			.priority = 0,
-++			.version = 0x6,
-++			.flow_lbl = { 0x00, 0x00, 0x00 },
-++			.payload_len = htons(BR_MC_ECHO_LEN),
-++			.nexthdr = IPPROTO_ICMPV6,
-++			.hop_limit = 1,
-++			.saddr = BR_MC_IN6_ZERO, // update
-++			.daddr = BR_MC_IN6_ZERO, // update
-++		},
-++		.echohdr = {
-++			.icmp6_type = ICMPV6_ECHO_REQUEST,
-++			.icmp6_code = 0,
-++			.icmp6_cksum = 0, // update
-++			.icmp6_dataun.u_echo = {
-++				.identifier = BR_MC_WAKEUP_ID,
-++				.sequence = 0,
-++			},
-++		},
-++	};
-++
-++	memcpy(&ip6_dst.s6_addr32[2], &eth_dst[0], ETH_ALEN / 2);
-++	memcpy(&ip6_dst.s6_addr[13], &eth_dst[3], ETH_ALEN / 2);
-++	ip6_dst.s6_addr[8] ^= 0x02;
-++	if (ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6_dst, 0,
-++			       &ip6_src))
-++		return NULL;
-++
-++	skb = netdev_alloc_skb_ip_align(br->dev, sizeof(*pkt));
-++	if (!skb)
-++		return NULL;
-++
-++	skb->protocol = htons(ETH_P_IPV6);
-++	skb->dev = port->dev;
-++
-++	pkt = (struct wakeupcall_pkt *)skb->data;
-++	*pkt = __pkt_template;
-++
-++	ether_addr_copy(pkt->ethhdr.h_source, br->dev->dev_addr);
-++	ether_addr_copy(pkt->ethhdr.h_dest, eth_dst);
-++
-++	pkt->ip6hdr.saddr = ip6_src;
-++	pkt->ip6hdr.daddr = ip6_dst;
-++
-++	csum_part = csum_partial(&pkt->echohdr, sizeof(pkt->echohdr), 0);
-++	csum = csum_ipv6_magic(&ip6_src, &ip6_dst, sizeof(pkt->echohdr),
-++			       IPPROTO_ICMPV6, csum_part);
-++	pkt->echohdr.icmp6_cksum = csum;
-++
-++	skb_reset_mac_header(skb);
-++	skb_set_network_header(skb, offsetof(struct wakeupcall_pkt, ip6hdr));
-++	skb_set_transport_header(skb, offsetof(struct wakeupcall_pkt, echohdr));
-++	skb_put(skb, sizeof(*pkt));
-++	__skb_pull(skb, sizeof(pkt->ethhdr));
-++
-++	return skb;
-++}
-++
-++void br_multicast_send_wakeupcall(struct timer_list *t)
-++{
-++	struct net_bridge_fdb_entry *fdb = from_timer(fdb, t, wakeupcall_timer);
-++	struct net_bridge_port *port = fdb->dst;
-++	struct net_bridge *br = port->br;
-++	struct sk_buff *skb, *skb0;
-++	int i;
-++
-++	skb0 = br_multicast_alloc_wakeupcall(br, port, fdb->key.addr.addr);
-++	if (!skb0)
-++		return;
-++
-++	for (i = port->wakeupcall_num_rings; i > 0; i--) {
-++		if (i > 1) {
-++			skb = skb_clone(skb0, GFP_ATOMIC);
-++			if (!skb) {
-++				kfree_skb(skb0);
-++				break;
-++			}
-++		} else {
-++			skb = skb0;
-++		}
-++
-++		NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
-++			dev_net(port->dev), NULL, skb, NULL, skb->dev,
-++			br_dev_queue_push_xmit);
-++	}
-++}
-++
-++static void
-++br_multicast_schedule_wakeupcalls(struct net_bridge_mcast *brmctx,
-++				  struct net_bridge_mcast_port *pmctx,
-++				  const struct in6_addr *group)
-++{
-++	struct net_bridge_fdb_entry *fdb;
-++	unsigned long delay;
-++
-++	rcu_read_lock();
-++	hlist_for_each_entry_rcu(fdb, &brmctx->br->fdb_list, fdb_node) {
-++		if (!fdb->dst || fdb->dst->dev != pmctx->port->dev)
-++			continue;
-++
-++		/* Wake-up calls to VLANs unsupported for now */
-++		if (fdb->key.vlan_id)
-++			continue;
-++
-++		/* Spread the ICMPv6 Echo Requests to avoid congestion.
-++		 * We then won't use a max response delay for the queries later,
-++		 * as that would be redundant. Spread randomly by a little less
-++		 * than max response delay to anticipate the extra round trip.
-++		 */
-++		delay =	ipv6_addr_any(group) ?
-++				brmctx->multicast_query_response_interval :
-++				brmctx->multicast_last_member_interval;
-++		delay = prandom_u32() % (3 * delay / 4);
-++
-++		timer_reduce(&fdb->wakeupcall_timer, jiffies + delay);
-++	}
-++	rcu_read_unlock();
-++}
-++#endif /* CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS */
-++
-+ static void __br_multicast_send_query(struct net_bridge_mcast *brmctx,
-+ 				      struct net_bridge_mcast_port *pmctx,
-+ 				      struct net_bridge_port_group *pg,
-+@@ -1655,6 +1823,13 @@ again_under_lmqt:
-+ 			dev_net(pmctx->port->dev), NULL, skb, NULL, skb->dev,
-+ 			br_dev_queue_push_xmit);
-+ 
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++		if (pmctx->port->wakeupcall_num_rings &&
-++		    group->proto == htons(ETH_P_IPV6))
-++			br_multicast_schedule_wakeupcalls(brmctx, pmctx,
-++							  &group->dst.ip6);
-++#endif
-++
-+ 		if (over_lmqt && with_srcs && sflag) {
-+ 			over_lmqt = false;
-+ 			goto again_under_lmqt;
-+@@ -3805,6 +3980,99 @@ int br_multicast_rcv(struct net_bridge_m
-+ 	return ret;
-+ }
-+ 
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++
-++static bool br_multicast_wakeupcall_check(struct net_bridge *br,
-++					  struct net_bridge_port *port,
-++					  struct sk_buff *skb, u16 vid)
-++{
-++	struct ethhdr *eth = eth_hdr(skb);
-++	const struct ipv6hdr *ip6h;
-++	unsigned int offset, len;
-++	struct icmp6hdr *icmp6h;
-++
-++	/* Wake-up calls to VLANs unsupported for now */
-++	if (!port->wakeupcall_num_rings || vid ||
-++	    eth->h_proto != htons(ETH_P_IPV6))
-++		return false;
-++
-++	if (!ether_addr_equal(eth->h_dest, br->dev->dev_addr) ||
-++	    is_multicast_ether_addr(eth->h_source) ||
-++	    is_zero_ether_addr(eth->h_source))
-++		return false;
-++
-++	offset = skb_network_offset(skb) + sizeof(*ip6h);
-++	if (!pskb_may_pull(skb, offset))
-++		return false;
-++
-++	ip6h = ipv6_hdr(skb);
-++
-++	if (ip6h->version != 6)
-++		return false;
-++
-++	len = offset + ntohs(ip6h->payload_len);
-++	if (skb->len < len || len <= offset)
-++		return false;
-++
-++	if (ip6h->nexthdr != IPPROTO_ICMPV6)
-++		return false;
-++
-++	skb_set_transport_header(skb, offset);
-++
-++	if (ipv6_mc_check_icmpv6(skb) < 0)
-++		return false;
-++
-++	icmp6h = (struct icmp6hdr *)skb_transport_header(skb);
-++	if (icmp6h->icmp6_type != ICMPV6_ECHO_REPLY ||
-++	    icmp6h->icmp6_dataun.u_echo.identifier != BR_MC_WAKEUP_ID)
-++		return false;
-++
-++	return true;
-++}
-++
-++static void br_multicast_wakeupcall_send_mldq(struct net_bridge_mcast *brmctx,
-++					      struct net_bridge_mcast_port *pmctx,
-++					      const u8 *eth_dst)
-++{
-++	const struct in6_addr group = BR_MC_IN6_ZERO;
-++	struct in6_addr ip6_dst;
-++	struct sk_buff *skb;
-++	u8 igmp_type;
-++
-++	/* we might have been triggered by multicast-address-specific query
-++	 * but reply with a general MLD query for now to keep things simple
-++	 */
-++	ipv6_addr_set(&ip6_dst, htonl(0xff020000), 0, 0, htonl(1));
-++
-++	skb = br_ip6_multicast_alloc_query(brmctx, pmctx, NULL, &ip6_dst,
-++					   &group, false, false, false,
-++					   &igmp_type, NULL, false);
-++	if (!skb)
-++		return;
-++
-++	skb->dev = pmctx->port->dev;
-++	ether_addr_copy(eth_hdr(skb)->h_dest, eth_dst);
-++
-++	br_multicast_count(brmctx->br, pmctx->port, skb, igmp_type,
-++			   BR_MCAST_DIR_TX);
-++	NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT,
-++		dev_net(pmctx->port->dev), NULL, skb, NULL, skb->dev,
-++		br_dev_queue_push_xmit);
-++}
-++
-++void br_multicast_wakeupcall_rcv(struct net_bridge_mcast *brmctx,
-++				 struct net_bridge_mcast_port *pmctx,
-++				 struct sk_buff *skb, u16 vid)
-++{
-++	if (!br_multicast_wakeupcall_check(brmctx->br, pmctx->port, skb, vid))
-++		return;
-++
-++	br_multicast_wakeupcall_send_mldq(brmctx, pmctx,
-++					  eth_hdr(skb)->h_source);
-++}
-++
-++#endif /* CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS */
-++
-+ static void br_multicast_query_expired(struct net_bridge_mcast *brmctx,
-+ 				       struct bridge_mcast_own_query *query,
-+ 				       struct bridge_mcast_querier *querier)
-+@@ -4333,6 +4601,15 @@ int br_multicast_set_vlan_router(struct
-+ 	return err;
-+ }
-+ 
-++int br_multicast_set_wakeupcall(struct net_bridge_port *p, unsigned long val)
-++{
-++	if (val > U8_MAX)
-++		return -EINVAL;
-++
-++	p->wakeupcall_num_rings = val;
-++	return 0;
-++}
-++
-+ static void br_multicast_start_querier(struct net_bridge_mcast *brmctx,
-+ 				       struct bridge_mcast_own_query *query)
-+ {
-+--- a/net/bridge/br_netlink.c
-++++ b/net/bridge/br_netlink.c
-+@@ -199,6 +199,9 @@ static inline size_t br_port_info_size(v
-+ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
-+ 		+ nla_total_size(sizeof(u8))	/* IFLA_BRPORT_MULTICAST_ROUTER */
-+ #endif
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++		+ nla_total_size(sizeof(u8))	/* IFLA_BRPORT_MCAST_WAKEUPCALL */
-++#endif
-+ 		+ nla_total_size(sizeof(u16))	/* IFLA_BRPORT_GROUP_FWD_MASK */
-+ 		+ nla_total_size(sizeof(u8))	/* IFLA_BRPORT_MRP_RING_OPEN */
-+ 		+ nla_total_size(sizeof(u8))	/* IFLA_BRPORT_MRP_IN_OPEN */
-+@@ -296,6 +299,11 @@ static int br_port_fill_attrs(struct sk_
-+ 			p->multicast_eht_hosts_cnt))
-+ 		return -EMSGSIZE;
-+ #endif
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++	if (nla_put_u8(skb, IFLA_BRPORT_MCAST_WAKEUPCALL,
-++		       p->wakeupcall_num_rings))
-++		return -EMSGSIZE;
-++#endif
-+ 
-+ 	/* we might be called only with br->lock */
-+ 	rcu_read_lock();
-+@@ -823,6 +831,7 @@ static const struct nla_policy br_port_p
-+ 	[IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 },
-+ 	[IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NLA_U8 },
-+ 	[IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NLA_U8 },
-++	[IFLA_BRPORT_MCAST_WAKEUPCALL] = { .type = NLA_U8 },
-+ 	[IFLA_BRPORT_MCAST_FLOOD] = { .type = NLA_U8 },
-+ 	[IFLA_BRPORT_BCAST_FLOOD] = { .type = NLA_U8 },
-+ 	[IFLA_BRPORT_VLAN_TUNNEL] = { .type = NLA_U8 },
-+@@ -950,6 +959,16 @@ static int br_setport(struct net_bridge_
-+ 		if (err)
-+ 			return err;
-+ 	}
-++#endif
-++
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++	if (tb[IFLA_BRPORT_MCAST_WAKEUPCALL]) {
-++		u8 wakeupcall = nla_get_u8(tb[IFLA_BRPORT_MCAST_WAKEUPCALL]);
-++
-++		err = br_multicast_set_wakeupcall(p, wakeupcall);
-++		if (err)
-++			return err;
-++	}
-+ #endif
-+ 
-+ 	if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) {
-+--- a/net/bridge/br_private.h
-++++ b/net/bridge/br_private.h
-+@@ -269,6 +269,10 @@ struct net_bridge_fdb_entry {
-+ 	unsigned long			used;
-+ 
-+ 	struct rcu_head			rcu;
-++
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++	struct timer_list		wakeupcall_timer;
-++#endif
-+ };
-+ 
-+ #define MDB_PG_FLAGS_PERMANENT	BIT(0)
-+@@ -382,6 +386,7 @@ struct net_bridge_port {
-+ 	u32				multicast_eht_hosts_limit;
-+ 	u32				multicast_eht_hosts_cnt;
-+ 	struct hlist_head		mglist;
-++	u8				wakeupcall_num_rings;
-+ #endif
-+ 
-+ #ifdef CONFIG_SYSFS
-+@@ -1419,6 +1424,21 @@ br_multicast_ctx_options_equal(const str
-+ }
-+ #endif
-+ 
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++void br_multicast_wakeupcall_rcv(struct net_bridge_mcast *brmctx,
-++				 struct net_bridge_mcast_port *pmctx,
-++				 struct sk_buff *skb, u16 vid);
-++void br_multicast_send_wakeupcall(struct timer_list *t);
-++int br_multicast_set_wakeupcall(struct net_bridge_port *p, unsigned long val);
-++#else
-++static inline void
-++br_multicast_wakeupcall_rcv(struct net_bridge_mcast *brmctx,
-++			    struct net_bridge_mcast_port *pmctx,
-++			    struct sk_buff *skb, u16 vid)
-++{
-++}
-++#endif /* CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS */
-++
-+ /* br_vlan.c */
-+ #ifdef CONFIG_BRIDGE_VLAN_FILTERING
-+ bool br_allowed_ingress(const struct net_bridge *br,
-+--- a/net/bridge/br_sysfs_if.c
-++++ b/net/bridge/br_sysfs_if.c
-+@@ -260,6 +260,21 @@ BRPORT_ATTR_FLAG(multicast_fast_leave, B
-+ BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST);
-+ #endif
-+ 
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++static ssize_t show_multicast_wakeupcall(struct net_bridge_port *p, char *buf)
-++{
-++	return sprintf(buf, "%d\n", p->wakeupcall_num_rings);
-++}
-++
-++static int store_multicast_wakeupcall(struct net_bridge_port *p,
-++				      unsigned long v)
-++{
-++	return br_multicast_set_wakeupcall(p, v);
-++}
-++static BRPORT_ATTR(multicast_wakeupcall, 0644, show_multicast_wakeupcall,
-++		   store_multicast_wakeupcall);
-++#endif
-++
-+ static const struct brport_attribute *brport_attrs[] = {
-+ 	&brport_attr_path_cost,
-+ 	&brport_attr_priority,
-+@@ -286,6 +301,9 @@ static const struct brport_attribute *br
-+ 	&brport_attr_multicast_fast_leave,
-+ 	&brport_attr_multicast_to_unicast,
-+ #endif
-++#ifdef CONFIG_BRIDGE_IGMP_SNOOPING_WAKEUPCALLS
-++	&brport_attr_multicast_wakeupcall,
-++#endif
-+ 	&brport_attr_proxyarp,
-+ 	&brport_attr_proxyarp_wifi,
-+ 	&brport_attr_multicast_flood,
-+--- a/net/core/rtnetlink.c
-++++ b/net/core/rtnetlink.c
-+@@ -55,7 +55,7 @@
-+ #include <net/net_namespace.h>
-+ 
-+ #define RTNL_MAX_TYPE		50
-+-#define RTNL_SLAVE_MAX_TYPE	41
-++#define RTNL_SLAVE_MAX_TYPE	42
-+ 
-+ struct rtnl_link {
-+ 	rtnl_doit_func		doit;
-+--- a/net/ipv6/mcast_snoop.c
-++++ b/net/ipv6/mcast_snoop.c
-+@@ -131,7 +131,7 @@ static inline __sum16 ipv6_mc_validate_c
-+ 	return skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo);
-+ }
-+ 
-+-static int ipv6_mc_check_icmpv6(struct sk_buff *skb)
-++int ipv6_mc_check_icmpv6(struct sk_buff *skb)
-+ {
-+ 	unsigned int len = skb_transport_offset(skb) + sizeof(struct icmp6hdr);
-+ 	unsigned int transport_len = ipv6_transport_len(skb);
-+@@ -150,6 +150,7 @@ static int ipv6_mc_check_icmpv6(struct s
-+ 
-+ 	return 0;
-+ }
-++EXPORT_SYMBOL(ipv6_mc_check_icmpv6);
-+ 
-+ /**
-+  * ipv6_mc_check_mld - checks whether this is a sane MLD packet
diff --git a/patches/openwrt/0006-ath79-don-t-create-DIR-825-B1-factory-image.patch b/patches/openwrt/0006-ath79-don-t-create-DIR-825-B1-factory-image.patch
deleted file mode 100644
index 4530f838dc9149682ebfd53551dfacd08a95cd17..0000000000000000000000000000000000000000
--- a/patches/openwrt/0006-ath79-don-t-create-DIR-825-B1-factory-image.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From: David Bauer <mail@david-bauer.net>
-Date: Mon, 21 Aug 2023 23:16:14 +0200
-Subject: ath79: don't create DIR-825 B1 factory image
-
-Currently the build fails for the D-Link DIR-825 B1. THis is due to the
-factory image being size-constrained. The sysupgrade image is not
-affected, as OpenWrt now uses a concatenated firmware partition.
-
-To newly install such a device, please use the latest OpenWrt 22.03
-factory image and install a Gluon sysupgrade.
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
-
-diff --git a/target/linux/ath79/image/generic.mk b/target/linux/ath79/image/generic.mk
-index f264c14314ca862e39c71e821621a81f8f1957fa..d3e2a9ccd7ae3cb03e35bb4b68a2360bb4d4f84c 100644
---- a/target/linux/ath79/image/generic.mk
-+++ b/target/linux/ath79/image/generic.mk
-@@ -1073,11 +1073,6 @@ define Device/dlink_dir-825-b1
-   DEVICE_PACKAGES := kmod-usb-ohci kmod-usb2 kmod-usb-ledtrig-usbport \
- 	kmod-leds-reset kmod-owl-loader kmod-switch-rtl8366s
-   IMAGE_SIZE := 7808k
--  FACTORY_SIZE := 6144k
--  IMAGES += factory.bin
--  IMAGE/factory.bin = append-kernel | pad-to $$$$(BLOCKSIZE) | append-rootfs | \
--	pad-rootfs | check-size $$$$(FACTORY_SIZE) | pad-to $$$$(FACTORY_SIZE) | \
--	append-string 01AP94-AR7161-RT-080619-00
- endef
- TARGET_DEVICES += dlink_dir-825-b1
- 
diff --git a/patches/openwrt/0007-mac80211-silence-warning-for-missing-rate-information.patch b/patches/openwrt/0007-mac80211-silence-warning-for-missing-rate-information.patch
deleted file mode 100644
index 668fca13130c71bd48506f851b723940f6b38153..0000000000000000000000000000000000000000
--- a/patches/openwrt/0007-mac80211-silence-warning-for-missing-rate-information.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From: David Bauer <mail@david-bauer.net>
-Date: Thu, 18 Jan 2024 00:52:09 +0100
-Subject: mac80211: silence warning for missing rate information
-
-Silence warnings for missing rate information.
-
-These warnings do not provide value. Instead, they might rotate more
-crucial information out of the kernel message ringbuffer.
-
-Link: https://github.com/freifunk-gluon/gluon/issues/3160
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
-
-diff --git a/package/kernel/mac80211/patches/subsys/999-silence-missing-rate.patch b/package/kernel/mac80211/patches/subsys/999-silence-missing-rate.patch
-new file mode 100644
-index 0000000000000000000000000000000000000000..a34455f78960ded59b60d3d9600823b39fc7b7a2
---- /dev/null
-+++ b/package/kernel/mac80211/patches/subsys/999-silence-missing-rate.patch
-@@ -0,0 +1,11 @@
-+--- a/net/mac80211/mesh_hwmp.c
-++++ b/net/mac80211/mesh_hwmp.c
-+@@ -350,7 +350,7 @@ u32 airtime_link_metric_get(struct ieee8
-+ 			return MAX_METRIC;
-+ 
-+ 		rate = ewma_mesh_tx_rate_avg_read(&sta->mesh->tx_rate_avg);
-+-		if (WARN_ON(!rate))
-++		if (!rate)
-+ 			return MAX_METRIC;
-+ 
-+ 		err = (fail_avg << ARITH_SHIFT) / 100;
diff --git a/patches/openwrt/0008-mac80211-add-AQL-support-for-broadcast-multicast-packets.patch b/patches/openwrt/0008-mac80211-add-AQL-support-for-broadcast-multicast-packets.patch
deleted file mode 100644
index a8f810850a415b32d0a6084cfd015dff84f57f3b..0000000000000000000000000000000000000000
--- a/patches/openwrt/0008-mac80211-add-AQL-support-for-broadcast-multicast-packets.patch
+++ /dev/null
@@ -1,317 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 9 Feb 2024 20:47:39 +0100
-Subject: mac80211: add AQL support for broadcast/multicast packets
-
-Should improve performance/reliability with lots of mcast packets
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
-(cherry picked from commit 95e633efbd1b4ffbbfc2d8abba2b05291f6e9903)
-
-diff --git a/package/kernel/mac80211/patches/subsys/330-mac80211-add-AQL-support-for-broadcast-packets.patch b/package/kernel/mac80211/patches/subsys/330-mac80211-add-AQL-support-for-broadcast-packets.patch
-new file mode 100644
-index 0000000000000000000000000000000000000000..5f6754e5024f90f7ba6833c3702fe3ce425c50bb
---- /dev/null
-+++ b/package/kernel/mac80211/patches/subsys/330-mac80211-add-AQL-support-for-broadcast-packets.patch
-@@ -0,0 +1,302 @@
-+From: Felix Fietkau <nbd@nbd.name>
-+Date: Fri, 9 Feb 2024 19:43:40 +0100
-+Subject: [PATCH] mac80211: add AQL support for broadcast packets
-+
-+Excessive broadcast traffic with little competing unicast traffic can easily
-+flood hardware queues, leading to throughput issues. Additionally, filling
-+the hardware queues with too many packets breaks FQ for broadcast data.
-+Fix this by enabling AQL for broadcast packets.
-+
-+Signed-off-by: Felix Fietkau <nbd@nbd.name>
-+---
-+
-+--- a/include/net/cfg80211.h
-++++ b/include/net/cfg80211.h
-+@@ -3158,6 +3158,7 @@ enum wiphy_params_flags {
-+ /* The per TXQ device queue limit in airtime */
-+ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L	5000
-+ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H	12000
-++#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC	50000
-+ 
-+ /* The per interface airtime threshold to switch to lower queue limit */
-+ #define IEEE80211_AQL_THRESHOLD			24000
-+--- a/net/mac80211/debugfs.c
-++++ b/net/mac80211/debugfs.c
-+@@ -215,11 +215,13 @@ static ssize_t aql_pending_read(struct f
-+ 			"VI     %u us\n"
-+ 			"BE     %u us\n"
-+ 			"BK     %u us\n"
-++			"BC/MC  %u us\n"
-+ 			"total  %u us\n",
-+ 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
-+ 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
-+ 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
-+ 			atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
-++			atomic_read(&local->aql_bc_pending_airtime),
-+ 			atomic_read(&local->aql_total_pending_airtime));
-+ 	return simple_read_from_buffer(user_buf, count, ppos,
-+ 				       buf, len);
-+@@ -245,7 +247,8 @@ static ssize_t aql_txq_limit_read(struct
-+ 			"VO	%u		%u\n"
-+ 			"VI	%u		%u\n"
-+ 			"BE	%u		%u\n"
-+-			"BK	%u		%u\n",
-++			"BK	%u		%u\n"
-++			"BC/MC	%u\n",
-+ 			local->aql_txq_limit_low[IEEE80211_AC_VO],
-+ 			local->aql_txq_limit_high[IEEE80211_AC_VO],
-+ 			local->aql_txq_limit_low[IEEE80211_AC_VI],
-+@@ -253,7 +256,8 @@ static ssize_t aql_txq_limit_read(struct
-+ 			local->aql_txq_limit_low[IEEE80211_AC_BE],
-+ 			local->aql_txq_limit_high[IEEE80211_AC_BE],
-+ 			local->aql_txq_limit_low[IEEE80211_AC_BK],
-+-			local->aql_txq_limit_high[IEEE80211_AC_BK]);
-++			local->aql_txq_limit_high[IEEE80211_AC_BK],
-++			local->aql_txq_limit_bc);
-+ 	return simple_read_from_buffer(user_buf, count, ppos,
-+ 				       buf, len);
-+ }
-+@@ -279,6 +283,11 @@ static ssize_t aql_txq_limit_write(struc
-+ 	else
-+ 		buf[count] = '\0';
-+ 
-++	if (sscanf(buf, "mcast %u", &q_limit_low) == 1) {
-++		local->aql_txq_limit_bc = q_limit_low;
-++		return count;
-++	}
-++
-+ 	if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
-+ 		return -EINVAL;
-+ 
-+--- a/net/mac80211/ieee80211_i.h
-++++ b/net/mac80211/ieee80211_i.h
-+@@ -1300,10 +1300,12 @@ struct ieee80211_local {
-+ 	u16 schedule_round[IEEE80211_NUM_ACS];
-+ 
-+ 	u16 airtime_flags;
-++	u32 aql_txq_limit_bc;
-+ 	u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
-+ 	u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
-+ 	u32 aql_threshold;
-+ 	atomic_t aql_total_pending_airtime;
-++	atomic_t aql_bc_pending_airtime;
-+ 	atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
-+ 
-+ 	const struct ieee80211_ops *ops;
-+--- a/net/mac80211/main.c
-++++ b/net/mac80211/main.c
-+@@ -789,6 +789,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
-+ 	spin_lock_init(&local->rx_path_lock);
-+ 	spin_lock_init(&local->queue_stop_reason_lock);
-+ 
-++	local->aql_txq_limit_bc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC;
-+ 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
-+ 		INIT_LIST_HEAD(&local->active_txqs[i]);
-+ 		spin_lock_init(&local->active_txq_lock[i]);
-+--- a/net/mac80211/sta_info.c
-++++ b/net/mac80211/sta_info.c
-+@@ -2164,13 +2164,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggre
-+ 
-+ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
-+ 					  struct sta_info *sta, u8 ac,
-+-					  u16 tx_airtime, bool tx_completed)
-++					  u16 tx_airtime, bool tx_completed,
-++					  bool mcast)
-+ {
-+ 	int tx_pending;
-+ 
-+ 	if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
-+ 		return;
-+ 
-++	if (mcast) {
-++		if (!tx_completed) {
-++			atomic_add(tx_airtime, &local->aql_bc_pending_airtime);
-++			return;
-++		}
-++
-++		tx_pending = atomic_sub_return(tx_airtime,
-++					       &local->aql_bc_pending_airtime);
-++		if (tx_pending < 0)
-++			atomic_cmpxchg(&local->aql_bc_pending_airtime,
-++				       tx_pending, 0);
-++		return;
-++	}
-++
-+ 	if (!tx_completed) {
-+ 		if (sta)
-+ 			atomic_add(tx_airtime,
-+--- a/net/mac80211/tx.c
-++++ b/net/mac80211/tx.c
-+@@ -2553,7 +2553,7 @@ static u16 ieee80211_store_ack_skb(struc
-+ 
-+ 		spin_lock_irqsave(&local->ack_status_lock, flags);
-+ 		id = idr_alloc(&local->ack_status_frames, ack_skb,
-+-			       1, 0x2000, GFP_ATOMIC);
-++			       1, 0x1000, GFP_ATOMIC);
-+ 		spin_unlock_irqrestore(&local->ack_status_lock, flags);
-+ 
-+ 		if (id >= 0) {
-+@@ -3957,20 +3957,20 @@ begin:
-+ encap_out:
-+ 	IEEE80211_SKB_CB(skb)->control.vif = vif;
-+ 
-+-	if (tx.sta &&
-+-	    wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
-+-		bool ampdu = txq->ac != IEEE80211_AC_VO;
-++	if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
-++		bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO;
-+ 		u32 airtime;
-+ 
-+ 		airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
-+ 							     skb->len, ampdu);
-+-		if (airtime) {
-+-			airtime = ieee80211_info_set_tx_time_est(info, airtime);
-+-			ieee80211_sta_update_pending_airtime(local, tx.sta,
-+-							     txq->ac,
-+-							     airtime,
-+-							     false);
-+-		}
-++		if (!airtime)
-++			return skb;
-++
-++		airtime = ieee80211_info_set_tx_time_est(info, airtime);
-++		info->tx_time_mc = !tx.sta;
-++		ieee80211_sta_update_pending_airtime(local, tx.sta, txq->ac,
-++						     airtime, false,
-++						     info->tx_time_mc);
-+ 	}
-+ 
-+ 	return skb;
-+@@ -4025,6 +4025,7 @@ struct ieee80211_txq *ieee80211_next_txq
-+ 	struct ieee80211_txq *ret = NULL;
-+ 	struct txq_info *txqi = NULL, *head = NULL;
-+ 	bool found_eligible_txq = false;
-++	bool aql_check;
-+ 
-+ 	spin_lock_bh(&local->active_txq_lock[ac]);
-+ 
-+@@ -4048,26 +4049,26 @@ struct ieee80211_txq *ieee80211_next_txq
-+ 	if (!head)
-+ 		head = txqi;
-+ 
-++	aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-++	if (aql_check)
-++		found_eligible_txq = true;
-++
-+ 	if (txqi->txq.sta) {
-+ 		struct sta_info *sta = container_of(txqi->txq.sta,
-+ 						    struct sta_info, sta);
-+-		bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-+-		s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
-+-
-+-		if (aql_check)
-+-			found_eligible_txq = true;
-+-
-+-		if (deficit < 0)
-++		if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) {
-+ 			sta->airtime[txqi->txq.ac].deficit +=
-+ 				sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
-+-
-+-		if (deficit < 0 || !aql_check) {
-+-			list_move_tail(&txqi->schedule_order,
-+-				       &local->active_txqs[txqi->txq.ac]);
-+-			goto begin;
-++			aql_check = false;
-+ 		}
-+ 	}
-+ 
-++	if (!aql_check) {
-++		list_move_tail(&txqi->schedule_order,
-++				   &local->active_txqs[txqi->txq.ac]);
-++		goto begin;
-++	}
-++
-+ 	if (txqi->schedule_round == local->schedule_round[ac])
-+ 		goto out;
-+ 
-+@@ -4132,7 +4133,8 @@ bool ieee80211_txq_airtime_check(struct
-+ 		return true;
-+ 
-+ 	if (!txq->sta)
-+-		return true;
-++		return atomic_read(&local->aql_bc_pending_airtime) <
-++		       local->aql_txq_limit_bc;
-+ 
-+ 	if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
-+ 		return true;
-+@@ -4181,15 +4183,15 @@ bool ieee80211_txq_may_transmit(struct i
-+ 
-+ 	spin_lock_bh(&local->active_txq_lock[ac]);
-+ 
-+-	if (!txqi->txq.sta)
-+-		goto out;
-+-
-+ 	if (list_empty(&txqi->schedule_order))
-+ 		goto out;
-+ 
-+ 	if (!ieee80211_txq_schedule_airtime_check(local, ac))
-+ 		goto out;
-+ 
-++	if (!txqi->txq.sta)
-++		goto out;
-++
-+ 	list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
-+ 				 schedule_order) {
-+ 		if (iter == txqi)
-+--- a/include/net/mac80211.h
-++++ b/include/net/mac80211.h
-+@@ -1092,6 +1092,7 @@ ieee80211_rate_get_vht_nss(const struct
-+  *	link the frame will be transmitted on
-+  * @hw_queue: HW queue to put the frame on, skb_get_queue_mapping() gives the AC
-+  * @ack_frame_id: internal frame ID for TX status, used internally
-++ * @tx_time_mc: TX time is for a multicast packet
-+  * @tx_time_est: TX time estimate in units of 4us, used internally
-+  * @control: union part for control data
-+  * @control.rates: TX rates array to try
-+@@ -1131,8 +1132,9 @@ struct ieee80211_tx_info {
-+ 	/* common information */
-+ 	u32 flags;
-+ 	u32 band:3,
-+-	    ack_frame_id:13,
-++	    ack_frame_id:12,
-+ 	    hw_queue:4,
-++	    tx_time_mc:1,
-+ 	    tx_time_est:10;
-+ 	/* 2 free bits */
-+ 
-+--- a/net/mac80211/sta_info.h
-++++ b/net/mac80211/sta_info.h
-+@@ -147,7 +147,8 @@ struct airtime_info {
-+ 
-+ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
-+ 					  struct sta_info *sta, u8 ac,
-+-					  u16 tx_airtime, bool tx_completed);
-++					  u16 tx_airtime, bool tx_completed,
-++					  bool mcast);
-+ 
-+ struct sta_info;
-+ 
-+--- a/net/mac80211/status.c
-++++ b/net/mac80211/status.c
-+@@ -716,7 +716,7 @@ static void ieee80211_report_used_skb(st
-+ 		ieee80211_sta_update_pending_airtime(local, sta,
-+ 						     skb_get_queue_mapping(skb),
-+ 						     tx_time_est,
-+-						     true);
-++						     true, info->tx_time_mc);
-+ 		rcu_read_unlock();
-+ 	}
-+ 
-+@@ -1127,10 +1127,11 @@ void ieee80211_tx_status_ext(struct ieee
-+ 		/* Do this here to avoid the expensive lookup of the sta
-+ 		 * in ieee80211_report_used_skb().
-+ 		 */
-++		bool mcast = IEEE80211_SKB_CB(skb)->tx_time_mc;
-+ 		ieee80211_sta_update_pending_airtime(local, sta,
-+ 						     skb_get_queue_mapping(skb),
-+ 						     tx_time_est,
-+-						     true);
-++						     true, mcast);
-+ 		ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0);
-+ 	}
-+ 
diff --git a/patches/packages/packages/0001-perl-don-t-build-in-parallel-and-bump-release.patch b/patches/packages/packages/0001-perl-don-t-build-in-parallel-and-bump-release.patch
index 2cf958edfc471c638d5d45ca54054d096135bc9b..de66e902b470de6dca5042a8d434a91de1dd42ce 100644
--- a/patches/packages/packages/0001-perl-don-t-build-in-parallel-and-bump-release.patch
+++ b/patches/packages/packages/0001-perl-don-t-build-in-parallel-and-bump-release.patch
@@ -8,10 +8,10 @@ https://github.com/openwrt/packages/issues/8238
 https://github.com/openwrt/packages/pull/17274
 
 diff --git a/lang/perl/Makefile b/lang/perl/Makefile
-index 2763de2777f8e3e60db9917ad64da7d5d127ba8f..7468e6db8ea561a6285b77990c8786ef3d46967f 100644
+index 6a6dd5ea86798e7e95e3657a94cca829dbd0924b..a0ffd0e982b59b871e683f745231579404a206c3 100644
 --- a/lang/perl/Makefile
 +++ b/lang/perl/Makefile
-@@ -34,8 +34,8 @@ PKG_BUILD_DIR:=$(BUILD_DIR)/perl/$(PKG_NAME)-$(PKG_VERSION)
+@@ -27,8 +27,8 @@ PKG_BUILD_DIR:=$(BUILD_DIR)/perl/$(PKG_NAME)-$(PKG_VERSION)
  HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/perl/$(PKG_NAME)-$(PKG_VERSION)
  PKG_INSTALL:=1
  PKG_BUILD_DEPENDS:=perl/host
diff --git a/patches/packages/routing/0002-batman-adv-Introduce-no-noflood-mark.patch b/patches/packages/routing/0002-batman-adv-Introduce-no-noflood-mark.patch
deleted file mode 100644
index 60a56752cee416c30a5a5f5b624fa3eda05ccc68..0000000000000000000000000000000000000000
--- a/patches/packages/routing/0002-batman-adv-Introduce-no-noflood-mark.patch
+++ /dev/null
@@ -1,176 +0,0 @@
-From: Linus Lüssing <linus.luessing@c0d3.blue>
-Date: Sat, 1 May 2021 22:19:03 +0200
-Subject: batman-adv: Introduce no noflood mark
-
-This mark prevents a multicast packet being flooded through the whole
-mesh. The advantage of marking certain multicast packets via e.g.
-ebtables instead of dropping is then the following:
-
-This allows an administrator to let specific multicast packets pass as
-long as they are forwarded to a limited number of nodes only and are
-therefore creating no burdon to unrelated nodes.
-
-Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
-
-diff --git a/batman-adv/patches/0034-batman-adv-Introduce-no-noflood-mark.patch b/batman-adv/patches/0034-batman-adv-Introduce-no-noflood-mark.patch
-new file mode 100644
-index 0000000000000000000000000000000000000000..8dbde75343f04fb3a643e300856ecfac7dc23e32
---- /dev/null
-+++ b/batman-adv/patches/0034-batman-adv-Introduce-no-noflood-mark.patch
-@@ -0,0 +1,156 @@
-+From 25b21382238c783298c0d8defc8c739126c1b54d Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue>
-+Date: Sat, 31 Mar 2018 03:36:19 +0200
-+Subject: [PATCH] batman-adv: Introduce no noflood mark
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+This mark prevents a multicast packet being flooded through the whole
-+mesh. The advantage of marking certain multicast packets via e.g.
-+ebtables instead of dropping is then the following:
-+
-+This allows an administrator to let specific multicast packets pass as
-+long as they are forwarded to a limited number of nodes only and are
-+therefore creating no burdon to unrelated nodes.
-+
-+Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
-+
-+---
-+
-+https://www.open-mesh.org/projects/batman-adv/wiki/Noflood-broadcast-prevention
-+
-+Changelog v2:
-+
-+* rebased to master
-+* sysfs -> netlink
-+---
-+ include/uapi/linux/batman_adv.h | 12 ++++++++++++
-+ net/batman-adv/netlink.c        | 22 ++++++++++++++++++++++
-+ net/batman-adv/soft-interface.c | 20 ++++++++++++++++++++
-+ net/batman-adv/types.h          | 12 ++++++++++++
-+ 4 files changed, 66 insertions(+)
-+
-+--- a/include/uapi/linux/batman_adv.h
-++++ b/include/uapi/linux/batman_adv.h
-+@@ -481,6 +481,18 @@ enum batadv_nl_attrs {
-+ 	 */
-+ 	BATADV_ATTR_MULTICAST_FANOUT,
-+ 
-++	/**
-++	 * @BATADV_ATTR_NOFLOOD_MARK: the noflood mark which allows to tag
-++	 *  frames which should never be broadcast flooded through the mesh.
-++	 */
-++	BATADV_ATTR_NOFLOOD_MARK,
-++
-++	/**
-++	 * @BATADV_ATTR_NOFLOOD_MASK: the noflood (bit)mask which allows to tag
-++	 *  frames which should never be broadcast flooded through the mesh.
-++	 */
-++	BATADV_ATTR_NOFLOOD_MASK,
-++
-+ 	/* add attributes above here, update the policy in netlink.c */
-+ 
-+ 	/**
-+--- a/net/batman-adv/netlink.c
-++++ b/net/batman-adv/netlink.c
-+@@ -134,6 +134,8 @@ static const struct nla_policy batadv_ne
-+ 	[BATADV_ATTR_AP_ISOLATION_ENABLED]	= { .type = NLA_U8 },
-+ 	[BATADV_ATTR_ISOLATION_MARK]		= { .type = NLA_U32 },
-+ 	[BATADV_ATTR_ISOLATION_MASK]		= { .type = NLA_U32 },
-++	[BATADV_ATTR_NOFLOOD_MARK]		= { .type = NLA_U32 },
-++	[BATADV_ATTR_NOFLOOD_MASK]		= { .type = NLA_U32 },
-+ 	[BATADV_ATTR_BONDING_ENABLED]		= { .type = NLA_U8 },
-+ 	[BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED]	= { .type = NLA_U8 },
-+ 	[BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED]	= { .type = NLA_U8 },
-+@@ -286,6 +288,14 @@ static int batadv_netlink_mesh_fill(stru
-+ 			bat_priv->isolation_mark_mask))
-+ 		goto nla_put_failure;
-+ 
-++	if (nla_put_u32(msg, BATADV_ATTR_NOFLOOD_MARK,
-++			bat_priv->noflood_mark))
-++		goto nla_put_failure;
-++
-++	if (nla_put_u32(msg, BATADV_ATTR_NOFLOOD_MASK,
-++			bat_priv->noflood_mark_mask))
-++		goto nla_put_failure;
-++
-+ 	if (nla_put_u8(msg, BATADV_ATTR_BONDING_ENABLED,
-+ 		       !!atomic_read(&bat_priv->bonding)))
-+ 		goto nla_put_failure;
-+@@ -466,6 +476,18 @@ static int batadv_netlink_set_mesh(struc
-+ 		bat_priv->isolation_mark_mask = nla_get_u32(attr);
-+ 	}
-+ 
-++	if (info->attrs[BATADV_ATTR_NOFLOOD_MARK]) {
-++		attr = info->attrs[BATADV_ATTR_NOFLOOD_MARK];
-++
-++		bat_priv->noflood_mark = nla_get_u32(attr);
-++	}
-++
-++	if (info->attrs[BATADV_ATTR_NOFLOOD_MASK]) {
-++		attr = info->attrs[BATADV_ATTR_NOFLOOD_MASK];
-++
-++		bat_priv->noflood_mark_mask = nla_get_u32(attr);
-++	}
-++
-+ 	if (info->attrs[BATADV_ATTR_BONDING_ENABLED]) {
-+ 		attr = info->attrs[BATADV_ATTR_BONDING_ENABLED];
-+ 
-+--- a/net/batman-adv/soft-interface.c
-++++ b/net/batman-adv/soft-interface.c
-+@@ -175,6 +175,23 @@ static void batadv_interface_set_rx_mode
-+ {
-+ }
-+ 
-++/**
-++ * batadv_send_skb_has_noflood_mark() - check if packet has a noflood mark
-++ * @bat_priv: the bat priv with all the soft interface information
-++ * @skb: the packet to check
-++ *
-++ * Return: True if the skb's mark matches a configured noflood mark and
-++ * noflood mark mask. False otherwise.
-++ */
-++static bool
-++batadv_skb_has_noflood_mark(struct batadv_priv *bat_priv, struct sk_buff *skb)
-++{
-++	u32 match_mark = skb->mark & bat_priv->noflood_mark_mask;
-++
-++	return bat_priv->noflood_mark_mask &&
-++	       match_mark == bat_priv->noflood_mark;
-++}
-++
-+ static netdev_tx_t batadv_interface_tx(struct sk_buff *skb,
-+ 				       struct net_device *soft_iface)
-+ {
-+@@ -325,6 +342,9 @@ send:
-+ 		if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb))
-+ 			brd_delay = msecs_to_jiffies(ARP_REQ_DELAY);
-+ 
-++		if (batadv_skb_has_noflood_mark(bat_priv, skb))
-++			goto dropped;
-++
-+ 		if (batadv_skb_head_push(skb, sizeof(*bcast_packet)) < 0)
-+ 			goto dropped;
-+ 
-+--- a/net/batman-adv/types.h
-++++ b/net/batman-adv/types.h
-+@@ -1635,6 +1635,18 @@ struct batadv_priv {
-+ 	 */
-+ 	u32 isolation_mark_mask;
-+ 
-++	/**
-++	 * @noflood_mark: the skb->mark value used to allow directed targeting
-++	 *  only
-++	 */
-++	u32 noflood_mark;
-++
-++	/**
-++	 * @noflood_mark_mask: bitmask identifying the bits in skb->mark to be
-++	 *  used for the noflood mark
-++	 */
-++	u32 noflood_mark_mask;
-++
-+ 	/** @bcast_seqno: last sent broadcast packet sequence number */
-+ 	atomic_t bcast_seqno;
-+ 
diff --git a/patches/packages/routing/0003-batctl-Add-noflood_mark-command.patch b/patches/packages/routing/0003-batctl-Add-noflood_mark-command.patch
deleted file mode 100644
index 6659bc3193d7db1e1e74bf817ab38fe9c26efd1d..0000000000000000000000000000000000000000
--- a/patches/packages/routing/0003-batctl-Add-noflood_mark-command.patch
+++ /dev/null
@@ -1,240 +0,0 @@
-From: Linus Lüssing <linus.luessing@c0d3.blue>
-Date: Sat, 1 May 2021 22:19:41 +0200
-Subject: batctl: Add noflood_mark command
-
-Adds support for the new 'noflood_mark' setting in batman-adv.
-
-Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
-
-diff --git a/batctl/patches/0012-batctl-Add-noflood_mark-command.patch b/batctl/patches/0012-batctl-Add-noflood_mark-command.patch
-new file mode 100644
-index 0000000000000000000000000000000000000000..1234c56cc0be080de8142f3a563cf4e070c4840a
---- /dev/null
-+++ b/batctl/patches/0012-batctl-Add-noflood_mark-command.patch
-@@ -0,0 +1,226 @@
-+From c14abebbeb4af76600cd6eb508e5e4e38a436b2f Mon Sep 17 00:00:00 2001
-+From: =?UTF-8?q?Linus=20L=C3=BCssing?= <linus.luessing@c0d3.blue>
-+Date: Fri, 26 Apr 2019 19:27:38 +0200
-+Subject: [PATCH] batctl: Add noflood_mark command
-+MIME-Version: 1.0
-+Content-Type: text/plain; charset=UTF-8
-+Content-Transfer-Encoding: 8bit
-+
-+Adds support for the new 'noflood_mark' setting in batman-adv.
-+
-+Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
-+---
-+Changelog v3:
-+* changed command from a noflood tri-state option
-+  to a value/mask one similar to the isolation mark
-+* noflood.c -> noflood_mark.c
-+
-+Changelog v2:
-+* added noflood.c
-+---
-+ Makefile       |   1 +
-+ README.rst     |  15 ++++++
-+ batman_adv.h   |  12 +++++
-+ man/batctl.8   |  23 ++++++++
-+ noflood_mark.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++++
-+ 5 files changed, 192 insertions(+)
-+ create mode 100644 noflood_mark.c
-+
-+--- a/Makefile
-++++ b/Makefile
-+@@ -69,6 +69,7 @@ $(eval $(call add_command,multicast_mode
-+ $(eval $(call add_command,neighbors,y))
-+ $(eval $(call add_command,neighbors_json,y))
-+ $(eval $(call add_command,network_coding,y))
-++$(eval $(call add_command,noflood_mark,y))
-+ $(eval $(call add_command,orig_interval,y))
-+ $(eval $(call add_command,originators,y))
-+ $(eval $(call add_command,originators_json,y))
-+--- a/README.rst
-++++ b/README.rst
-+@@ -419,6 +419,21 @@ Example::
-+ 
-+ 
-+ 
-++batctl noflood_mark
-++=======================
-++
-++display or modify noflood_mark setting
-++
-++Usage::
-++
-++  batctl noflood_mark|nf $value[/0x$mask]
-++
-++* Example 1: ``batctl nf 0x00000001/0xffffffff``
-++* Example 2: ``batctl nf 0x00040000/0xffff0000``
-++* Example 3: ``batctl nf 16``
-++* Example 4: ``batctl nf 0x0f``
-++
-++
-+ batctl translocal
-+ -----------------
-+ 
-+--- a/batman_adv.h
-++++ b/batman_adv.h
-+@@ -481,6 +481,18 @@ enum batadv_nl_attrs {
-+ 	 */
-+ 	BATADV_ATTR_MULTICAST_FANOUT,
-+ 
-++	/**
-++	 * @BATADV_ATTR_NOFLOOD_MARK: the noflood mark which allows to tag
-++	 *  frames which should never be broadcast flooded through the mesh.
-++	 */
-++	BATADV_ATTR_NOFLOOD_MARK,
-++
-++	/**
-++	 * @BATADV_ATTR_NOFLOOD_MASK: the noflood (bit)mask which allows to tag
-++	 *  frames which should never be broadcast flooded through the mesh.
-++	 */
-++	BATADV_ATTR_NOFLOOD_MASK,
-++
-+ 	/* add attributes above here, update the policy in netlink.c */
-+ 
-+ 	/**
-+--- /dev/null
-++++ b/noflood_mark.c
-+@@ -0,0 +1,140 @@
-++// SPDX-License-Identifier: GPL-2.0
-++/* Copyright (C) 2009-2019  B.A.T.M.A.N. contributors:
-++ *
-++ * Antonio Quartulli <a@unstable.cc>
-++ * Linus Lüssing <linus.luessing@c0d3.blue>
-++ *
-++ * License-Filename: LICENSES/preferred/GPL-2.0
-++ */
-++
-++#include <errno.h>
-++#include <stddef.h>
-++#include <stdint.h>
-++#include <string.h>
-++
-++#include "main.h"
-++#include "sys.h"
-++
-++static struct noflood_mark_data {
-++	uint32_t noflood_mark;
-++	uint32_t noflood_mask;
-++} noflood_mark;
-++
-++static int parse_noflood_mark(struct state *state, int argc, char *argv[])
-++{
-++	struct settings_data *settings = state->cmd->arg;
-++	struct noflood_mark_data *data = settings->data;
-++	char *mask_ptr;
-++	char buff[256];
-++	uint32_t mark;
-++	uint32_t mask;
-++	char *endptr;
-++
-++	if (argc != 2) {
-++		fprintf(stderr, "Error - incorrect number of arguments (expected 1)\n");
-++		return -EINVAL;
-++	}
-++
-++	strncpy(buff, argv[1], sizeof(buff));
-++	buff[sizeof(buff) - 1] = '\0';
-++
-++	/* parse the mask if it has been specified, otherwise assume the mask is
-++	 * the biggest possible
-++	 */
-++	mask = 0xFFFFFFFF;
-++	mask_ptr = strchr(buff, '/');
-++	if (mask_ptr) {
-++		*mask_ptr = '\0';
-++		mask_ptr++;
-++
-++		/* the mask must be entered in hex base as it is going to be a
-++		 * bitmask and not a prefix length
-++		 */
-++		mask = strtoul(mask_ptr, &endptr, 16);
-++		if (!endptr || *endptr != '\0')
-++			goto inval_format;
-++	}
-++
-++	/* the mark can be entered in any base */
-++	mark = strtoul(buff, &endptr, 0);
-++	if (!endptr || *endptr != '\0')
-++		goto inval_format;
-++
-++	data->noflood_mask = mask;
-++	/* erase bits not covered by the mask */
-++	data->noflood_mark = mark & mask;
-++
-++	return 0;
-++
-++inval_format:
-++	fprintf(stderr, "Error - incorrect number of arguments (expected 1)\n");
-++	fprintf(stderr, "The following formats for mark(/mask) are allowed:\n");
-++	fprintf(stderr, " * 0x12345678\n");
-++	fprintf(stderr, " * 0x12345678/0xabcdef09\n");
-++	return -EINVAL;
-++}
-++
-++static int print_noflood_mark(struct nl_msg *msg, void *arg)
-++{
-++	struct nlattr *attrs[BATADV_ATTR_MAX + 1];
-++	struct nlmsghdr *nlh = nlmsg_hdr(msg);
-++	struct genlmsghdr *ghdr;
-++	int *result = arg;
-++
-++	if (!genlmsg_valid_hdr(nlh, 0))
-++		return NL_OK;
-++
-++	ghdr = nlmsg_data(nlh);
-++
-++	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0),
-++		      genlmsg_len(ghdr), batadv_netlink_policy)) {
-++		return NL_OK;
-++	}
-++
-++	if (!attrs[BATADV_ATTR_NOFLOOD_MARK] ||
-++	    !attrs[BATADV_ATTR_NOFLOOD_MASK])
-++		return NL_OK;
-++
-++	printf("0x%08x/0x%08x\n",
-++	       nla_get_u32(attrs[BATADV_ATTR_NOFLOOD_MARK]),
-++	       nla_get_u32(attrs[BATADV_ATTR_NOFLOOD_MASK]));
-++
-++	*result = 0;
-++	return NL_STOP;
-++}
-++
-++static int get_noflood_mark(struct state *state)
-++{
-++	return sys_simple_nlquery(state, BATADV_CMD_GET_MESH,
-++				  NULL, print_noflood_mark);
-++}
-++
-++static int set_attrs_noflood_mark(struct nl_msg *msg, void *arg)
-++{
-++	struct state *state = arg;
-++	struct settings_data *settings = state->cmd->arg;
-++	struct noflood_mark_data *data = settings->data;
-++
-++	nla_put_u32(msg, BATADV_ATTR_NOFLOOD_MARK, data->noflood_mark);
-++	nla_put_u32(msg, BATADV_ATTR_NOFLOOD_MASK, data->noflood_mask);
-++
-++	return 0;
-++}
-++
-++static int set_noflood_mark(struct state *state)
-++{
-++	return sys_simple_nlquery(state, BATADV_CMD_SET_MESH,
-++				  set_attrs_noflood_mark, NULL);
-++}
-++
-++static struct settings_data batctl_settings_noflood_mark = {
-++	.data = &noflood_mark,
-++	.parse = parse_noflood_mark,
-++	.netlink_get = get_noflood_mark,
-++	.netlink_set = set_noflood_mark,
-++};
-++
-++COMMAND_NAMED(SUBCOMMAND, noflood_mark, "nf", handle_sys_setting,
-++	      COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK,
-++	      &batctl_settings_noflood_mark,
-++	      "[mark]            \tdisplay or modify noflood_mark setting");
diff --git a/scripts/copy_output.lua b/scripts/copy_output.lua
index 1af2f8f7bbe4442348f71671d272032d4a02c91d..11de8d26b65f2fe63a7ddfc4a58472b0362e9766 100755
--- a/scripts/copy_output.lua
+++ b/scripts/copy_output.lua
@@ -92,5 +92,6 @@ if (env.GLUON_DEVICES or '') == '' then
 	lib.exec {'rm', '-f', dest_dir('\0')..'/\0'}
 	lib.exec({'rmdir', '-p', dest_dir('\0')}, true, '2>/dev/null')
 	mkdir(dest_dir(package_prefix))
+	lib.exec {'rm', '-rf', 'openwrt/bin/targets/'..bindir..'/packages/tmp'}
 	lib.exec {'cp', 'openwrt/bin/targets/'..bindir..'/packages/\0', dest_dir(package_prefix)}
 end
diff --git a/targets/generic b/targets/generic
index 5a2f73a956e35ee487b15120bdb0b37905523fa1..6db126515dc5a3cb9c92410696cf1cf2a376fb7e 100644
--- a/targets/generic
+++ b/targets/generic
@@ -59,10 +59,13 @@ try_config('TARGET_SQUASHFS_BLOCK_SIZE', 256)
 
 config('KERNEL_PROC_STRIPPED', true)
 config('KERNEL_AIO', false)
-config('KERNEL_IO_URING', false)
+try_config('KERNEL_IO_URING', false)
 config('KERNEL_FHANDLE', false)
 config('KERNEL_FANOTIFY', false)
 config('KERNEL_CGROUPS', false)
+-- KERNEL_NAMESPACES is required for procd-ujail
+-- This option is enabled by default on non small flash targets
+config('KERNEL_NAMESPACES', true)
 config('KERNEL_IP_MROUTE', false)
 config('KERNEL_IPV6_MROUTE', false)
 config('KERNEL_IPV6_SEG6_LWTUNNEL', false)
diff --git a/targets/mediatek-mt7622 b/targets/mediatek-mt7622
index 6becbfc0d50db09bfe6d3c3440be4deffcc853cf..81440d5008283307693a1276fd99a545ba3361d3 100644
--- a/targets/mediatek-mt7622
+++ b/targets/mediatek-mt7622
@@ -13,9 +13,3 @@ device('ubiquiti-unifi-6-lr-v1', 'ubnt_unifi-6-lr-v1', {
 	manifest_aliases = {'ubiquiti-unifi-6-lr'}, -- Upgrade from OpenWrt 22.03
 })
 
-
--- Xiaomi
-
-device('xiaomi-redmi-router-ax6s', 'xiaomi_redmi-router-ax6s', {
-	factory = false,
-})
diff --git a/targets/ramips-mt7621 b/targets/ramips-mt7621
index 90cf3a4c7be8225e8184961347df1edc26f360f8..10c9d19fcf0f8913a009a210cd6333c1ad0e396e 100644
--- a/targets/ramips-mt7621
+++ b/targets/ramips-mt7621
@@ -166,17 +166,3 @@ device('zyxel-wsm20', 'zyxel_wsm20', {
 	factory = false,
 })
 
-
--- Devices without WLAN
-
--- Ubiquiti
-
-device('ubiquiti-edgerouter-x', 'ubnt_edgerouter-x', {
-	factory = false,
-	packages = {'-hostapd-mini'},
-})
-
-device('ubiquiti-edgerouter-x-sfp', 'ubnt_edgerouter-x-sfp', {
-	factory = false,
-	packages = {'-hostapd-mini'},
-})