v2.4.1
Fixed
-
WireGuard peer writes now reach the kernel. A
POST,PUTorPATCHonnetwork/wireguard_peerscommitted the section to uci, answered200, and left the running tunnel untouched, so the peer did not exist as far as the kernel was concerned until something else restarted the interface.DELETEhad the matching hole: it answered204and the peer stayed live, so revoking a peer through the API did not revoke its access. Operators who have deleted a peer through a release before this one and need that revocation to have taken effect should confirm withwg show <interface> peers.The cause is a platform-wide limitation rather than anything specific to uapi. netifd reads peer sections with
config_foreach wireguard_<iface>inside the proto setup step, so a peer edit leaves the parentinterfacesection unchanged,/etc/init.d/network reloadfinds nothing to converge, and does nothing at all. LuCI is affected too and documents the workaround in its own peer form ("Restart wireguard interface to apply changes"), because its Save and Apply resolves to that samenetwork reloadthrough aconfig.changeevent that carries only a package name and so cannot express which interface changed. uapi knows which resource was written, and therefore which interface is affected, so it applies the change itself.Each peer write is now pushed to the kernel with
wg setafter the commit: a set for a create or update, a remove for a delete, a remove for a peer being disabled, and a remove of the previous key before the set when aPUTrotatespublic_key. This is a new external command alongside the reload,apkandpasswdcalls uapi already makes. It is deliberate: WireGuard exposes no ubus service for peers, so netifd shells out towgand so does LuCI's own backend. Asking netifd to re-apply instead was implemented and rejected on measurement, because the ubus call returns before the work happens and a failure takes the whole interface down: a single peer with an unresolvableendpoint_hostdropped a working tunnel and its healthy peers while the API answered200. Withwg seta bad peer fails alone, synchronously, and the write rolls back with the reason reported. uci remains the only config writer and the applied state is derived from committed uci, never from the request.endpoint_hostis shell-quoted rather than newly validated, so no previously accepted payload starts being rejected; a preshared key is passed as a0600file and never as an argument.route_allowed_ipsis applied too, with the routes spelled the same way netifd spells them and placed inip4table/ip6tablewhen the interface sets one; a prefix is withdrawn only once no remaining peer and noconfig routesection still wants it. Peers on an interface that is down, or that netifd does not know, are written to uci and applied at the nextifup, as before. Closes openwrt-iac/uapi#51. -
network/interfacesno longer discards half of a body that setsipaddrandipaddrsto different addresses. Both are wire names for the same ucilist ipaddr, and the list won whenever it was non-empty, so the scalar was dropped and the write answered200with the old address read back: a caller re-reading saw its own change vanish rather than fail.The answer differs by method, because the methods differ in what a caller can express.
POSTandPATCHreport422 validation_failedwith aconflictonipaddr: naming both there is a choice, andPATCHcan say which one it meant.PUTcannot. A full-replace caller sends every field it knows, the read mirrors the first list entry intoipaddr, so the scalar sits in its state even when its own config named only the list, and one of the two is stale by construction on every apply. Rejecting that body madeipaddrsunwritable through any such client. OnPUTa differingipaddris therefore dropped in favour of the list, which is the precedence the write path already applied, so no uci outcome changes.Carve-out (
docs/versioning.md): thePOSTandPATCHrejection refuses a payload earlier releases accepted, and ships in a patch because the state it produced was one no caller could rely on. The request said two different things about one option and was told neither had been ignored.The same collision made
PATCHnaming onlyipaddra silent no-op, since the merge folded the just-readipaddrsinto the body and that won. Whichever of the two the caller actually names now wins, and the other is dropped rather than resurrected from the server's own read. Closes openwrt-iac/uapi#60 and openwrt-iac/uapi#65. -
network/wireguard_peersaccepts IPv6 and bare addresses inallowed_ips. The field required IPv4 CIDR notation, so every IPv6 peer was refused and a dual-stack tunnel could not be configured through the API at all, and a bare address was refused even though it is the formwg showprints back and netifd turns into a host route. All four shapeswgaccepts (10.0.0.0/24,10.0.0.5,fd00::/64,fd00::1, and the0.0.0.0/0and::/0catch-alls) are now accepted, checked againstwg seton a real interface, and whatwgrejects is still rejected. This only widens what is accepted, so no payload that worked before stops working. -
network/rulesaccepts a packet mark as the only selector. It required one ofin/out/src/dest, butmarkis a selector in its own right and the one policy routing of reply traffic depends on: firewall4 marks in mangle prerouting and the rule sends the mark to a table, with neither source nor destination knowable in advance.ip rule add fwmark 0x43 lookup 43is valid, netifd writes exactly that from a rule carrying onlymark,lookupandpriority, and the kernel prints it back asfrom all fwmark 0x43. The check prevented nothing, since the workaround was to addsrc: "0.0.0.0/0", which is what a mark-only rule already means. Closes openwrt-iac/uapi#52.