* Enhance nftables rules to include connection tracking direction
- Added rules to track the direction of original and reply packets in the nftables configuration for better handling of TCP traffic on port 443.
- Updated existing rules to incorporate connection tracking direction, ensuring that original and reply packets are processed correctly.
- Adjusted the conditions for packet acceptance and queuing to improve traffic management and performance.
A TCP port filter that covers client source ports makes b4 skip outgoing TCP
Fixes#429
Domain-only sets leave the first segment of Chrome's split ClientHello untouched
Fixes#430
A TCP Fast Open SYN that carries a ClientHello is fragmented like a data segment
Fixes#431
b4's own fakes and fragments count toward the per-connection queue window
Fixes#432
* issue-427-428 (#435)
* feat(config): add TCP port overlap detection with ephemeral ranges
* feat(direction): implement queue direction handling and add tests
* Enhance packet direction handling and flow management in TUN
- Introduced a new `Direction` type in the `engine` package to represent packet directions.
- Updated `firstNLimiter` to return packet direction alongside admission status.
- Enhanced flow counting logic to track the direction of TCP flows (original vs reply).
- Implemented direction-based packet forwarding in the `Engine` to ensure correct routing.
- Added tests to verify the correct handling of packet directions and flow records.
- Refactored existing tests to utilize the new direction handling logic.
- Introduced new utility functions for determining TCP direction based on conntrack rules.
* fix(queue): queue router traffic once, clamp MSS before the queue, give b4's own packets their own window
- Router-local packets were queued twice, at OUTPUT and again at POSTROUTING
(regression since 1.26.1): b4 now jumps from OUTPUT only when device
filtering moves the B4 jump to FORWARD.
- nftables: the MSS clamp rules ran after the queue rules, so queued SYN-ACKs
and router SYNs were never clamped (since 1.39.1); they now lead prerouting,
output and forward, as on iptables.
- Packets b4 sends into a connection carry a mark bit of their own (0x2000000).
The OUTPUT CONNMARK save no longer tags those connections with the queue
mark (regression since 1.71.1): their SYN-ACKs and RSTs reach b4 again, so
RST protection, RST escalation and TTL learning work on them; their server
data stays unqueued unless a set uses an incoming mode.
- #432: connections b4 has sent into get the window again on top (2N TCP, 2M
UDP), so its own fakes and fragments no longer use up the client's packets.
Diagnostics and the installer compare a flowtable guard with 2*max(N,M).
- TCP to private destinations is accepted unprocessed again (regression since
1.22.0): inbound connections to port-forwarded LAN servers broke when a set
matched.
- nftables keeps counting the reply window in original packets; the port
classifier warning gets its own dashboard event.
* fix(quic): a QUIC drop holds for the whole connection, not only the queue window (#428)
When a set drops or rejects a QUIC connection (udp.mode drop or reject, or a
block set), b4 now keeps it dropped past the first datagrams the queue rules
hand it. Before, a connection whose first datagram had been let through (a
ClientHello split over two Initials, as Chrome sends it, decided on the
second) used up its queue window on the dropped datagrams and the rest
reached the server, so a blocked site could open over QUIC.
- nftables: the flow goes into a timed set (b4_udp_drop4/6, 120 s) dropped
in b4_chain ahead of the UDP window rule.
- iptables, TUN with xt_connbytes, and alongside the nftables set: b4
deletes the flow's conntrack entry through ctnetlink, so its later
datagrams start a fresh entry inside the window and reach b4 again. Every
delete names a complete tuple and carries a mark guard that turns a
tuple-less request into a no-op instead of a table flush; a self-test
decides once whether conntrack netlink is usable.
- TUN without xt_connbytes: the engine's own packet count lets a dropped
connection through to b4 past its limit.
- System Info shows whether drops hold (nf_conntrack_netlink,
kmod-nf-conntrack-netlink on OpenWrt, which the installer recommends).
* fix(queue): drop the wider window, hold only real QUIC, keep held flows decidable, docs
- Revert the #432 window extension (2N/2M on connections b4 sent into) and
the TUN doubling for tracked re-sends: letting the strategies reach client
data past about 10 KB put the default pastseq fakes inside the stream and
broke TLS uploads through a default-strategy set (measured 21-26 of 80),
and the combo delay hit the first request after the handshake. #432 stays
open until fakes cannot land inside the stream.
- The return for connections b4 sent into matches bit 0x2000000 with the
queue mark clear, so foreign whole-value marks that carry both bits are not
taken for b4's.
- A QUIC drop holds a flow only when the datagram is QUIC or belongs to a
QUIC flow b4 named; other UDP to a learned address is dropped as before.
- nftables: the drop set is written from a background writer over one
netlink socket, waiting behind another nf_tables transaction is no longer
a failure, and a settings change empties the drop sets (with conntrack
netlink) so held connections are decided again.
- RST protection judges the first connection to an address b4 has not
learned yet by the SYN-ACK it saw before the connection matched a set.
- A queue mark containing bit 0x2000000 is warned about.
- The iptables monitor checks for a TCP queue rule on one listing line;
System Info lists nf_conntrack_netlink only for iptables; make test-netns
runs the conntrack netlink tests after the tables tests.
- Docs EN/RU, AI topics and changelog for the queue direction, the marks,
router traffic, the MSS clamp order, QUIC drops and private destinations.
* fix(queue): direction match on duplication rules, private guard only without an address match, refuse a new queue mark with bit 25
From the Copilot review of #438.
- Duplication rules carry the ORIGINAL direction match wherever the
kernel has it: B4 (-m conntrack --ctdir ORIGINAL), b4_chain (ct
direction original) and TUN's duplication steer rules. Without it a
wide port filter let a reply from a duplication address, such as a
local service answering a client in that range, be queued at
POSTROUTING or steered into the TUN and duplicated as client
traffic, since the hook and the TUN take such packets as original.
The TUN tests that kept these rules port-only now require the match.
- The private-destination guard in handleTCPPacket applies only when
no set's IP targets cover the address on that port. A set that lists
10.0.0.0/8 keeps its strategy, as before this branch; SNI-matched
and port-only sets still leave port-forwarded LAN servers alone.
- Saving a queue mark with bit 0x2000000 as a new value is refused
(queue.mark, mark_conflict). A configuration that already holds one
keeps loading and saving with the warning, so an upgrade does not
stop the service.
- Docs (EN, RU): capture.md and marks.md drop the duplication
exemption from the direction text, the zapret note follows (its
untracked packets are no longer queued toward duplication
addresses), marks.md lists the refusal, matching.md the exception
for listed private addresses.
* fix(config): refuse a mark with bit 25 only where NFQUEUE mode newly takes it
From Copilot's review of 50e71425: the save-time refusal also turned
down a TUN configuration's mark such as 0x2008000, although TUN adds
0x10000000 to its packets and never 0x2000000, and loading such a
configuration from disk only warns in NFQUEUE mode. The refusal now
applies when a save leaves NFQUEUE mode with a mark that has bit
0x2000000 and the running configuration did not already run NFQUEUE
mode with that mark, so leaving TUN mode with one is still refused.
marks.md (EN, RU) says NFQUEUE mode.
* docs: what an ASUS connection-mark rewrite does to b4's replies
Measured on an ASUS RT-BE88U with AiProtection on: the firmware writes
its own value into the connection mark of device connections from their
start, and the values of all 24 test connections had bit 15, the
default queue mark. The return for b4's own connections in
B4_PREROUTING then skipped all 235 replies of 6 HTTPS connections and
all 6 answers of DNS queries to an outside resolver. marks.md and the
three AI topics said the server's packets are queued as on any other
connection in that case, which holds only for values without the queue
bit, such as the 0x10C3D seen with QoS on. They now say the firmware
can remove bit 0x2000000, and that a value with every queue-mark bit
hides the connection's replies, the SYN-ACK and resets included.
marks.md (EN, RU) also says that b4 refuses a queue mark with bit
0x2000000 when a save would put it on NFQUEUE mode for the first time,
instead of accepting it with a warning.
* test(ctnl): refuse to flush conntrack when the namespace check cannot prove isolation
From Copilot's review of 35cea3d9: with B4_NETNS_TEST=1 and root, a
failed lookup of /proc/self/ns/net or /proc/1/ns/net skipped the
isolation check, and the tuple-less delete test could then flush the
conntrack table of the namespace it ran in, the host's included.
Failing closed on any failed lookup would refuse make test-netns
itself: inside unshare -Urnm, /proc/1/ns/net answers EACCES. When a
lookup fails, the tests now refuse to run if any interface other than
loopback is up. A fresh namespace passes: the kernel's fallback tunnel
devices (gre0, sit0, tunl0 and the like) are created down. Checked: the
rig passes, and the same rig with a dummy interface up is refused.
* docs(capture): the drop set holds by addresses and ports
From Copilot's review of 0dcc9308: on nftables, a new QUIC connection
that a device opens from the same UDP port to the same server address
and port within the 2 minutes is dropped by the same set element,
whatever name it carries. It takes a domain-only set and a client that
reuses one UDP socket for several connections. Exempting Initials from
the drop set and deleting the element when b4 accepts a replacement
would change the kernel rules and weaken the hold on routers without
nf_conntrack_netlink, so the code stays and internals/capture.md (EN,
RU) states the limit.
* fix(nfq): Match any IP address does not count as listing a private address
From the final review of #438: since 50e71425 the private-destination
guard skipped TCP to a private address whenever any address target of
a set matched it. A set with Match any IP address (0.0.0.0/0, ::/0)
and no source devices matches every private address, so a ClientHello
from the internet to a port-forwarded LAN server got that set's
strategy again, and a name match by another set could learn the LAN
server's address.
The guard now lets TCP to a private address through to processing only
when the matched set's most specific network holding it lies inside
the private ranges (SuffixSet.MatchedIPNet, utils.IsPrivateNet): a set
that lists 10.0.0.0/8 or 192.168.1.50 keeps its strategy, the catch-all
does not.
The test cases cover the catch-all, a private range next to the
catch-all, and assert that an untouched ClientHello is neither logged
nor read, so the "port the set leaves out" case now fails when the
guard moves above the port check (checked with go test -overlay on
both mutations).
Docs (EN, RU): matching.md states the exception and what b4 learns,
dscp.md and routing.md name the private-address exception for learned
destinations, and the dscp.enabled topic follows. The port-forward
changelog bullet drops its last sentence, a caveat on a FIXED line.
* fix(nfq): an overlapping private target on another port no longer hides the set that takes the port
From Copilot's review of dde38ff0: the private-destination guard asked
only about the set the address match picked, after the port check had
cleared it. With set A listing 10.1.2.3/32 on port 8443 and set B
listing 10.0.0.0/8 and the ClientHello's domain on 443, A won the
address match, the port check cleared it, and the guard accepted the
packet before the name match could pick B. Without the guard (main) B
handled it.
SuffixSet.ListsPrivateTCPTarget answers the guard's question directly:
does any set that applies to the device (source devices, IP family)
list the destination through a private network and take the port. If
so, b4 handles the connection as usual; Match any IP address still
lists no private address. It replaces MatchedIPNet.
Tests: the matcher case table (overlap, port, catch-all, source device,
IP family, IPv6) and the handler overlap case; the overlap and
catch-all cases both fail with the guard reverted to "no address
match" (go test -overlay). matching.md (EN, RU) says a set that
applies to the device lists the address, and that b4, not
necessarily that set, then handles the connection as usual.
* Queue connections whose foreign mark only contains the queue mark
KeeneticOS writes whole marks such as 0x0ffffaaa on the packets of the
devices it lists for its hotspot. Such a mark has every bit of the
default queue mark 0x8000, so the queue chains (B4 since #418, b4_chain
always) and the DNS query rules returned those packets as b4's own, and
the devices got no strategy (discussion #276).
- tables: B4, b4_chain, B4_RAW, the nft raw chains and B4_DNSCOPY take a
mark for b4's own only when it has every queue-mark bit and nothing
outside 0x71027fff, the bits b4 itself adds (0x8000/0x8efd8000 by
default). The iptables spec drops a full mask the way iptables lists
it, so the monitor finds the rule. DNS over TCP keeps 0x8000/0x8000,
since b4's own upstream connection picking up a foreign bit would
loop through the redirect. The OUTPUT accept, the connmark rules, the
routing chains and B4_TUN are unchanged.
- nfq: a leaving packet that a socket on the router sent with every
queue-mark bit is released unchanged (NFQA_UID, kernel 3.14+), so
b4's own packets and the router's that another service adds a bit to
keep their handling; the DNS copy listener ignores such copies from
mangle OUTPUT. go-nfqueue sends queue flags to the kernel only in
RegisterWithErrorFunc, so the queue registers synchronously and falls
back to the old flags on any error (3.6 to 3.13 reject UID_GID).
- nfq: a connection whose foreign mark sends it through another
interface than b4's injected packets would take (RTM_GETROUTE with
the queue mark, compared with OutDev) is handed off like a tunnel,
reason reroute-><iface>; so is an IPv4 one for which b4's packets
have no route. iptables checks every foreign mark; nftables and an
external firewall (Skip Setup) only marks with every queue-mark bit,
because other tables' output chains can re-mark b4's packets there.
The scope follows the backend each AddRules applies
(tables.BackendAppliedFunc). On an interface one of b4's own sets
routes through, the lookup carries the packet's route bits, so the
set's own connections keep their strategy.
- Lookups run in the background, at most four at a time, because
RTM_GETROUTE takes RTNL on kernels before 4.14. A packet waits at most
250 ms after its lookup started and gets no answer, as before, when
every slot is busy. Answers are cached 5 s per destination and mark;
one that disagrees with the packet's interface is reused for that
interface for 1 s, so a handed-off connection costs one lookup. A
kernel refusal keeps the netlink socket, a lost reply closes it.
* Docs and changelog for the hotspot marks and the reroute hand-off
- marks.md (EN, RU): the queue chains and the DNS query rules match
0x8000/0x8efd8000, defined right after the rules table, and the free
bits paragraph names both 0x8efd8000 matches; DNS over TCP keeps
0x8000/0x8000; the queue row names the socket-owner release and the
reroute hand-off with its nftables and Skip Setup scope; KeeneticOS,
MagiTrickle and mwan3 entries updated, and MagiTrickle's bit
0x40000000 no longer reads as if it were on b4's injected packets.
- internals/capture.md (EN, RU): the exact match and the socket-owner
release; the tunnel-egress section becomes "Packets that leave by
another route" with the reroute check, its IPv4 no-route case, the
route bits used on an interface b4's own sets route through, and its
scope; xray.md links to it.
- changelog.md, changelog_ru.md (1.87.0): the tunnel CHANGED bullet
covers the reroute hand-off and names other services as its subject,
since sets b4 routes itself keep their strategy; the FIXED bullet for
foreign marks names the KeeneticOS hotspot devices and links
discussion #276.
* Narrow the reroute check to cases b4 can predict
Follow-up to the review of #440.
- The every-mark reroute check relies on b4's OUTPUT accept to stop
other services' rules, which holds only for legacy iptables. With the
iptables-nft variant a service's native nft output chain still
re-marks b4's packets, so tables.OwnPacketsShielded checks the
variant (xtIsNftVariant); the engine start and the applied-backend
hook use it.
- An IPv4 blackhole route answers EINVAL. It now counts as "b4's
packets have no route", like ENETUNREACH, EHOSTUNREACH and EACCES.
- The lookup mark no longer follows interface membership alone. A
packet's route bits are used only when they equal the route mark of
one of b4's own interface sets on that interface; tables publishes
those marks as a lock-free snapshot (RoutedIfaceMarks) whenever its
routing cache changes. Another service's mark that its own rule
routes to that interface, such as mwan3's 0x200, is looked up with
the queue mark alone and handed off when b4's packets would leave
elsewhere.
- Docs (EN, RU) and the changelog name legacy iptables and the
iptables-nft case.
* Keep the narrow reroute check under Skip Setup and beside nftables
Follow-up to the second Copilot review of #440.
- A start with Skip IPTables/NFTables Setup on already checks only
marks with every queue-mark bit (backend "external"). Switching the
setting on at runtime reports no backend, so a router that started
on legacy iptables kept checking every foreign mark, although b4 no
longer refreshes its rules and cannot count on its OUTPUT accept.
reroutedEgress now reads the setting from the live config.
- b4's legacy OUTPUT accept does not stop native nftables chains, so a
service with its own nftables tables (OpenWrt's fw4, anything on
iptables-nft) can still re-mark b4's packets next to legacy iptables.
OwnPacketsShielded now also requires that nft lists no ip, ip6 or
inet table other than b4's own (b4_*, _b4*).
- Docs (EN, RU) state the one setup where every mark is checked.
* Recheck the reroute scope at every monitor check
Follow-up to the third Copilot review of #440.
- The every-mark reroute check was decided only when b4 applied its
rules, so nftables tables that another service added (or removed)
later went unseen while b4's own rules stayed in place. tables now
publishes the decision itself (OwnPacketsShieldFunc, replacing
BackendAppliedFunc) from addRules and from every NFQUEUE monitor
check that finds b4's applied rules intact, using the applied
backend. That includes a check that leaves a newer configuration to
a refresh waiting for a Discovery run, which can last the whole run.
With the monitor off (interval 0) the scope still follows rule
applies.
- TestTheRouteMarksOfInterfaceSetsArePublished saved the route-mark
snapshot after publishing its fixture, so its cleanup restored the
fixture. familyResetGlobals now saves and restores the snapshot and
starts every routing test from an empty one.
- Docs (EN, RU) say when b4 looks for other services' nftables tables.
* Hand off a routed set's connection whose mark replaced the set's
Follow-up to asovetov's KeeneticOS test on #440.
KeeneticOS writes its whole hotspot mark over b4's route mark, so a
hotspot device's connection to an address of an interface set leaves
by KeeneticOS's policy route (fwmark 0xffffaaa lookup 4096), while
the set's output rule sends the packets b4 injects through the set's
interface. Since #440 queues these devices, a set whose egress is not
WireGuard or a TUN device applied its strategy there, and split
segments could leave by another interface than the connection. Before
#440 these packets were never queued.
handsOffEgress now also covers routed sets: a set that routes to an
interface hands the connection off (reroute-><iface>) when the packet
carries a mark with every queue-mark bit and not the route mark of a
b4 set on the set's egress. Marks without the queue-mark bits keep
their handling, and Discovery's marks are excluded. The SYN and
ClientHello paths and QUIC all follow it.
Docs (EN, RU): the KeeneticOS entry of the marks guide says that
KeeneticOS saves the mark into the connection mark, so b4 skips these
devices' replies, that interface sets do not route them and that a
proxy set can take them; the capture page covers the routed-set
hand-off.
* Keep the ordinary check for interface sets that route nothing
Follow-up to the Copilot review of 93ef62b.
A set with Domain-only matching and no IP targets routes no address:
every routing writer skips learned targets, and RoutingHandsOffPackets
already keeps its strategy. The routed-set hand-off still treated it
as routing, so a hotspot device's connection lost a strategy that
works when its policy route and b4's own packets share an uplink.
handsOffEgress now gives such a set the tunnel and reroute checks of a
set without routing, while other routed modes (proxy, block) keep no
egress check, as before. The domain-only test moves into
SetConfig.RoutesNoAddress, shared with RoutingHandsOffPackets.
Docs (EN, RU) name the exception.
* Hand off a hotspot mark while any interface set routes elsewhere
Follow-up to the Copilot review of 3770807.
The output rule of an interface set re-marks the packets b4 injects to
the set's addresses onto the set's route, whichever set matched the
connection. A mark with every queue-mark bit (KeeneticOS's hotspot
mark) replaces b4's route mark on the connection itself, so the
connection follows KeeneticOS's policy while b4's packets to an
address of any interface set follow that set. The route lookup cannot
see b4's output rules, and nfq cannot see which addresses the kernel
sets hold. For such a mark b4 now hands the connection off while an
enabled interface set routes addresses through another interface than
the connection leaves by, and otherwise runs the ordinary tunnel and
reroute checks. This replaces the per-set check of 93ef62b and also
covers a set routed through the connection's own interface, a
non-routing set that wins over an interface set by SNI, and port-only
or Domain-only sets, which route nothing and do not count. Marks
without the queue-mark bits keep their handling, and proxy and block
sets mark none of b4's injected packets.
SetConfig.RoutesAddressesToInterface replaces RoutesNoAddress and is
shared with RoutingHandsOffPackets.
Docs (EN, RU) describe the rule.
* feat(connection): enhance connection handling with SNI matching and learned sets
* feat(tls): enhance connection decision handling and cache management
* feat(tls): enhance TLS connection handling with hello sequence tracking and decision management
* feat(connection): improve connection takeover logic and handling of payloadless injections
* fix(changelog): update release date to 2026-10-15 and fix domain-only matching application
* feat(tls): update decision logic to track last seen timestamp and add new test for connection state retention
* fix(connection): improve domain-only matching logic and enhance connection handling for port sets
* feat(tls): enhance tlsInfo structure and update decision logic to include source MAC address
* fix(clienthello): enhance validation logic for ClientHello payload and add tests for encrypted handshake records
* fix(handler): enhance connection set logic to handle payload and sequence number for ClientHello
* test(tls): add test for ClientReset behavior after ClientHello processing
* fix(tls): update tlsInfo and tlsInfoCache to include generation tracking for connection decisions
* Implement ECH key stripping for IPv6 DNS answers when IPv6 support is disabled
- Refactor DNS packet processing to utilize a new `replyDomain` function for domain extraction.
- Update DNS TCP server to check for IP family support using `QueuesIPFamily` method.
- Introduce `handleECHOnlyPacket` method to manage ECH key stripping logic.
- Modify NFQueue handling to incorporate ECH key removal based on configuration settings.
- Enhance iptables and nftables rule management to queue DNS answers for ECH key removal when IPv6 support is off.
- Add tests to validate ECH key stripping behavior for IPv6 DNS answers and ensure proper rule application in iptables and nftables.
* Enhance IPv6 Support and DNS Handling
- Updated documentation to clarify the behavior of b4 with IPv6 support disabled, specifying that DNS answers over IPv6 are processed in NFQUEUE mode when ECH key removal is enabled.
- Modified core settings documentation to reflect changes in IPv6 handling, ensuring clarity on how b4 interacts with IPv6 packets and DNS responses.
- Improved Russian translations to align with the updated English documentation regarding IPv6 support and DNS processing.
- Adjusted the DNS TCP server implementation to handle IPv6 link-local addresses correctly, ensuring they are not redirected.
- Enhanced the ECH key stripping logic to account for unknown devices, ensuring that device-scoped sets do not interfere with IPv6 responses.
- Updated tests to verify the correct handling of IPv6 DNS answers and the behavior of the system when IPv6 support is toggled.
- Refined the iptables and nftables management to ensure proper rule application and monitoring for IPv6 DNS traffic.
* Refactor DNS handling for IPv6: streamline ECH key stripping and enhance device matching logic
* Fix issues with manual device handling and regex patterns in DHCP and SNI matching
* regex memo, zone fix
* Check each family's DNS answer rule in the nft monitor in ECH-only mode
With one address family enabled and the other queued only to remove ECH keys, both families' answer rules share the inet chains. The monitor matched only an unqualified sport 53 rule and the ECH-only family's rule, so losing the enabled family's answer rule went unnoticed while the other remained. It now requires both families' answer rules in prerouting and output.
* Correct the ECH notes carried into the restructured docs
Name the per-set condition for IPv6 ECH removal on the IPv4 / IPv6 field and the --ipv6 flag, state that NFQUEUE mode writes IPv6 rules for DNS while a set removes ECH keys, add Intercept DNS over TCP to the DNSSEC troubleshooting fix, split long sentences in How b4 handles DNS, and fix the Russian wording for TUN and captured interfaces.
* Добавлены новые разделы документации на русском языке:
- Интеграции: описание подключения b4 к внешним сервисам, включая IPinfo и AI-помощника.
- Решение проблем: таблица с симптомами и ссылками на соответствующие страницы для устранения неполадок.
- Особенности платформ: проблемы, характерные для OpenWrt, Keenetic и MikroTik, с их решениями.
- Сообщение о проблеме: инструкции по сбору информации для отчета об ошибке и что может быть личным.
- b4 не видит трафик: причины, по которым трафик не доходит до b4, и способы их устранения.
* docs: update installation and quickstart sections with community sets information and ad-blocking test pages
* docs: update various sections for clarity and detail, including Discovery, Unblocking, Integrations, MCP, System, DC Relay, and Reporting
* docs: enhance quickstart and troubleshooting sections with additional details on browser connections and DNS handling
* docs: clarify Discovery and Unblocking sections regarding IP version handling and QUIC connections
* fix(tables): leave b4's answers to devices out of the CONNMARK save
b4 sends DNS answers, resets and ICMP toward a device with the queue
mark plus 0x20000000. The mangle OUTPUT CONNMARK save (nftables: the
output ct mark set) matched any packet carrying every queue-mark bit,
so such an answer put the queue mark into the device's connection
entry, and the connmark RETURN in B4_PREROUTING then skipped every
later packet on that socket, the resolver's replies included. The save
now wants 0x20000000 clear unless the queue mark has that bit itself,
and teardown also deletes the old rule spec.
* fix(dns): send an answer that settles a queued reply straight to the device
When a resolver's reply made its set escalate, b4 answered through the
copy listener's deliver, so a pending claim for the same query could
take the answer while the reply itself was dropped, and the device got
nothing. The answers that settle a queued reply (a pin or the redirect
of the escalated set, a SERVFAIL, a further escalation) now go to the
device directly; the query and copy paths keep using deliver.
* fix(tables): have the nft monitor check the DNS-over-TCP return and redirect
The monitor took the ip6 table b4_dnsnat6 for the ip table b4_dnsnat,
since it matched the table name as a substring, and it only looked for
the word "redirect" in the table listing. A deleted IPv4 table next to
a live IPv6 one, or a deleted mark return in dnstcp_pre or dnstcp_out,
went unnoticed, and without that return b4's own DNS over TCP to port
53 loops into its listener. The monitor now matches the table line
exactly and wants, in both chains, the return for the queue mark ahead
of the redirect to the listener port.
* fix(dns): learn the addresses heal puts into an answer
When every address in an answer was unreachable, heal answered with an
address remembered from an earlier lookup, but b4 learned only the
answer's original addresses. A routing set or a set's own DSCP value
did not get the substitute, the reply was not held for it, and the
device could reach it outside the set's route. The reply, redirect and
DNS-over-TCP paths now also learn the addresses the filter added (host
hints, DNS names, routing and DSCP) and wait for them like the rest.
* docs: KeeneticOS on newer kernels and the CONNMARK save for answers to devices
On KeeneticOS with a kernel from 4.18 (4.14.173) on, b4 queues DNS
queries in mangle instead of copying them, which the DNS page and the
Keenetic install page did not say. The marks guide shows the CONNMARK
save that leaves packets toward devices alone, and the changelog
bullet for device connections marked by b4's answers covers the
answers b4 sends itself as well.
* test(nfq): wait for the stopped copy reader before counting its warning
TestDeadCopyListenerIsReopened waited only for the listener to read as
dead, which the reader marks before it logs the stop, so under load the
log was flushed and counted before the warning was written ("the stop
was logged 0 times"). It now waits for the reader to return first.
* fix(docs): clarify NAT handling for source port changes in DNS responses
* fix(tables): refactor DNS TCP redirect rule handling and enhance tests for various scenarios
* Implement DNS kernel handling and testing
- Added `dnskernel.go` to manage DNS kernel facts, including KeeneticOS detection and twin query handling.
- Introduced `dnskernel_test.go` for comprehensive testing of DNS kernel functionalities, including version parsing and KeeneticOS detection scenarios.
- Created `cleanup_test.go` to ensure stale artifacts are cleared correctly, particularly focusing on the presence of raw tables in the cleanup process. Ответ AAAA теряется при одновременных A+AAAA-запросах клиента, пока активны правила очереди UDP 53 (iptables, Netcraze/Keenetic)
Fixes#402
* feat(dns): add support for TCP in DNS copy rules
* Add support for DSCP marking in iptables and ip6tables rules
- Updated iptables and ip6tables rules to include handling for the DSCP mark 0x8000.
- Added checks to ensure packets marked with DSCP are processed correctly without being queued again.
- Introduced new tests to validate the behavior of marked packets in network namespaces.
- Ensured that the queue-mark RETURN rule is prioritized in the chain to prevent conflicts in conntrack.
* test(dns): add tests for claim enforcement during DNS copying
* fix(dns): improve logging and diagnostics for NFLOG listener status and DNS query handling
* fix(docs): clarify handling of DNS requests from port 53 in documentation
* feat(tables): implement mark return handling and add tests for DNS query placement
* feat(dns): enhance DNS claim handling and logging for query limits
* feat(dns): enhance mark return handling in iptables and tests
* fix(tests): improve validation for DNS query handling in raw table tests
* fix(dns): rewrite filtered DNS replies in place instead of re-sending them
When b4 changed a resolver's reply (ECH key removal, IPv4-fallback AAAA
stripping, heal), it dropped the queued reply and sent the changed answer
from its raw socket. That packet passed the mangle OUTPUT CONNMARK save
and marked the device's conntrack entry with the queue mark, so later
packets on the same socket skipped b4. The reply is now rewritten in
place and held for its routes like any other answer; b4 drops it and
sends its own packet only when the queue cannot carry a modified packet,
and that send no longer goes through the copy listener, so a pending
claim cannot swallow it. Copy-listener log lines no longer say replies
pass unchanged.
* fix(tables): keep B4_DNSTCP's return first and have the monitor check it
Since #420 the DNS-over-TCP redirect is also installed for sets that only
remove ECH keys. Its mark RETURN was appended and unchecked, so a lone
deletion went unnoticed and a later restore put it below the REDIRECT,
looping b4's own TCP DNS upstream into its listener. The RETURN is now
inserted, dnsTCPRulesPresent requires the exact rule, and System Info's
rule dump lists nat B4_DNSTCP.
* docs: describe copy mode with ECH removal and in-place reply rewrites
Copied-request replies without b4's own answer still go through the
ordinary reply handling (ECH key removal, IPv4 fallback, heal); the ECH
section lists the copy-mode replies that keep the key; MCP topics and the
DNS card hint cover copy mode and the TCP redirect for strip_ech sets;
changelog entry for the re-send that marked device connections; swagger
regenerated for 1.87.0.
* Add ECH support and DNS nameserver configuration
- Implement ECH (Encrypted Client Hello) parsing and stripping in the DNS package.
- Introduce tests for ECH response parsing and validation.
- Add functionality to read system nameservers from /etc/resolv.conf, with a maximum of three nameservers.
- Create tests for nameserver parsing to ensure correct behavior with various input configurations.
- Implement ECH stripping logic in the nfq package, allowing for conditional removal of ECH keys based on configuration.
- Add tests to validate ECH stripping behavior under different scenarios, including device-specific rules and discovery queue handling.
* Refactor ECH handling and improve DNS response processing
- Updated the Russian documentation for clarity on ECH key handling in DNS responses.
- Enhanced the AI topics JSON to provide clearer descriptions of DNS-related configurations.
- Improved ECH probing logic to log when an ECH key is published but not usable.
- Modified ECH renewal logic to track cover names more effectively.
- Added tests to ensure correct behavior when ECH keys are rotated and when unusable keys are handled.
- Refined DNS response parsing to correctly identify and return public names for ECH keys.
- Updated the UI to display multiple ECH cover names instead of just one.
- Adjusted ECH stripping logic to handle multiple covers in DNS responses.
- Enhanced tests for ECH stripping to ensure correct behavior when multiple keys are present.
* feat(dns): implement ECH upstream handling and improve query processing
* feat(ech): enhance ECH probing with concurrent lookups and improve test coverage
* feat(dns): enhance ECH public name validation and improve handling of IPv4 literals in tests
* feat(ech): add support for 'ech_strip' verdict family in SetVerdictCard component
* feat(dns): add support for AXFR and IXFR DNS query types in ipblock
* feat(discovery): enhance ECH handling with improved failure detection and logging
* feat(dns): improve svcbECH function to handle malformed records and ensure key order validation
* feat(dns): add TSIG record handling in response processing and enhance ECH tests
* feat(discovery): enhance ECH rejection handling and add tests for unverified rejections
* feat(dns): add support for KX, SRV, and DNAME record types in DNS response handling
* feat(dns): enhance ECH handling with additional service binding parameters and compatibility checks
* feat(discovery): update ECH handling description to clarify key lookup behavior in local cache scenarios
* Add comprehensive tests for routing and tproxy functionalities
- Introduced `routing_async_await_test.go` to validate asynchronous DNS handling, ensuring proper address addition and waiting mechanisms.
- Added `routing_async_batch_test.go` to test batching behavior for DNS updates, confirming that bursts of updates are coalesced and handled correctly.
- Created `listener_latehello_test.go` to verify the handling of connections and names during the dial process, including scenarios with delayed upstream responses.
- Implemented `listener_lognames_test.go` to ensure connection lines are logged accurately, capturing domain names and metadata under various conditions. Имя сайта
Fixes#378
* Enhance DNS handling and routing mechanisms
- Implemented graceful shutdown for the NFQueue pool by releasing held answers.
- Updated DNS worker to handle pinned answers more effectively, ensuring responses wait for route availability.
- Modified route handling to account for failures during asynchronous operations, improving reliability.
- Enhanced tests for DNS answer handling, ensuring correct behavior during route updates and failures.
- Refactored proxy naming logic to improve efficiency and clarity in handling client connections.
Имя сайта
Fixes#378
* fix(routing): stop updates queued before a resync from reinstalling or reverting sets
A DNS-learning batch queued before a routing resync, a forced resync or RoutingClearAll carried its old config and could reinstall a removed set or rebuild a changed one onto its old state, and an answer after the resync could join it. Batches now carry a generation; a stale one only adds its addresses into the set as installed, and only while the set still covers the targets the answer came from. The forced resync holds the routing lock from the cache reset through the rebuild.
* fix(routing): mark queued updates stale inside the routing lock
The resync bumped the batch generation before it took routeMu, so a batch could take the lock in between, see itself stale and add its addresses to the set as it stood before the resync. The bump now runs in routingSyncConfigLocked, which both the regular and the forced resync call under routeMu, so a stale batch only ever sees the state after the resync.
* fix(routing,tproxy): check expanded geosite domains for queued answers, name from the unread rest only between relay reads
A stale DNS-learning batch now also needs every domain the set used to expand to, so a geodata reload that drops a domain from a category drops answers queued for it.
The final naming pass joins the bytes the relay has read with the bytes still queued only while the relay is between reads, so it never glues across a chunk the relay holds between its read and observe. Otherwise it retries every 10 ms up to five times and then logs what the relay read.
* fix(routing): forget install failures on a full routing reset
RoutingClearAll reset the async state but kept routeInstallFailedAt, so a set that failed to install shortly before a reset was still treated as failed afterwards and its first DNS answers went out without waiting for the set update.