mirror of
https://github.com/DanielLavrushin/b4.git
synced 2026-08-17 13:03:49 +00:00
fix: ensure UDP rules are rebuilt when toggling upstream UDP setting
This commit is contained in:
parent
c7123219cc
commit
ce49086ee0
4 changed files with 24 additions and 1 deletions
|
|
@ -5,6 +5,7 @@
|
|||
- FIXED: **b4's own output filled a router's memory** - init scripts from older installers wrote b4's full log to a file kept in RAM on OpenWrt, and updates never replaced those scripts.
|
||||
- FIXED: **A restart could leave a second copy of b4 running alongside the first** - the departing process deleted the file that tracks the running service, after its replacement had already claimed it.
|
||||
- FIXED: **Log files kept growing while b4 ran** - the error log was checked against its size limit only at startup, the update log was never trimmed, and abandoned trace files were left behind.
|
||||
- FIXED: **Turning "Route UDP through upstream" off left the UDP rules in the firewall and took the listener away** - the rule state was keyed on the upstream host, port and username alone, so the switch never rebuilt the firewall side, while the listener was rebuilt without its UDP half. Matched UDP kept being diverted to a port with nothing behind it, so a browser speaking HTTP/3 to a routed site sat through its timeout rather than falling back to TCP, and switching the option off made the site less reachable than leaving it on. Only a restart applied the change.
|
||||
- FIXED: **Traffic handed to an upstream SOCKS5 proxy went out direct once for every address behind a domain** - a set matches by domain suffix, so `ipinfo.io` covers `website-cdn.assets.ipinfo.io`, while the address pre-resolve only looked up the names written in the set. Every other hostname was learned one address at a time from connections that had already left unrouted, and a site spread across a CDN kept producing them. A hostname that matches a routing set by suffix is resolved in full, and every address it answers with enters the set.
|
||||
- FIXED: **The first connection after a redirected DNS answer could escape a routing set** - b4 sent the answer to the client and only then wrote the addresses into the set, and writing them runs the `nft` or `ipset` binary, which takes longer than the client takes to send the packet that follows its DNS answer.
|
||||
- ADDED: **A Remove button for each geo database** - a downloaded `geosite.dat` or `geoip.dat` could only be replaced, never taken off the device, and pointing the destination directory somewhere else left the previous copy at the old path, since editing that field alone changes nothing to save. On a router with little free storage that meant two copies of a 51 MB file with no way to reach either from the interface. Remove deletes the file and clears its path and source URL, so the scheduler does not fetch it back, and a download or upload into a different directory deletes the copy b4 wrote at the old location.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
- ИСПРАВЛЕНО: **Собственный вывод b4 заполнял память роутера** - init-скрипты старых установщиков писали полный лог b4 в файл, который на OpenWrt хранится в оперативной памяти, а обновления эти скрипты не заменяли.
|
||||
- ИСПРАВЛЕНО: **Перезапуск мог оставить вторую копию b4 работать рядом с первой** - уходящий процесс удалял файл, по которому отслеживается запущенная служба, уже после того, как его занял преемник.
|
||||
- ИСПРАВЛЕНО: **Файлы логов росли всё время работы b4** - лог ошибок сверялся с пределом размера только при запуске, лог обновлений не подрезался вовсе, а брошенные файлы трассировки оставались на диске.
|
||||
- ИСПРАВЛЕНО: **Выключение «Направлять UDP через вышестоящий прокси» оставляло правила UDP в фаерволе и забирало слушателя** - состояние правил определялось только адресом, портом и именем пользователя вышестоящего прокси, поэтому переключатель не перестраивал сторону фаервола, тогда как слушатель пересоздавался уже без своей UDP-половины. Совпавший UDP по-прежнему заворачивался на порт, за которым ничего нет, поэтому браузер, говорящий с маршрутизируемым сайтом по HTTP/3, высиживал тайм-аут вместо отката на TCP, а выключение опции делало сайт менее доступным, чем её включённое состояние. Изменение применялось только после перезапуска.
|
||||
- ИСПРАВЛЕНО: **Трафик, передаваемый вышестоящему прокси SOCKS5, уходил напрямую по одному разу на каждый адрес за доменом** - сет сопоставляет по суффиксу домена, поэтому `ipinfo.io` покрывает `website-cdn.assets.ipinfo.io`, а предварительное разрешение адресов искало только имена, записанные в самом сете. Все прочие имена узнавались по одному адресу за раз из соединений, уже ушедших мимо маршрута, и сайт, размазанный по CDN, продолжал их порождать. Имя, совпавшее с маршрутным сетом по суффиксу, разрешается целиком, и все его адреса попадают в сет.
|
||||
- ИСПРАВЛЕНО: **Первое соединение после перенаправленного DNS-ответа могло проскочить мимо маршрутного сета** - b4 отправлял ответ клиенту и только затем записывал адреса в сет, а такая запись вызывает бинарник `nft` или `ipset`, которому нужно больше времени, чем клиенту на отправку пакета следом за DNS-ответом.
|
||||
- ДОБАВЛЕНО: **Кнопка удаления для каждой базы геоданных** - скачанный `geosite.dat` или `geoip.dat` можно было только заменить, но не убрать с устройства, а смена директории назначения оставляла прежнюю копию по старому пути, ведь правка одного этого поля не даёт ничего, что можно сохранить. На роутере с малым объёмом памяти это означало две копии файла в 51 МБ, до которых из интерфейса было не добраться. Удаление стирает файл и очищает его путь и URL источника, поэтому планировщик не скачивает базу обратно, а скачивание или загрузка в другую директорию стирает копию, записанную b4 по прежнему пути.
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ func buildRouteState(cfg *config.Config, set *config.SetConfig) routeState {
|
|||
st.mark = mark
|
||||
st.table = proxyTable()
|
||||
st.tproxyPort = port
|
||||
st.upstreamKey = fmt.Sprintf("%s:%d|%s", set.Routing.Upstream.Host, set.Routing.Upstream.Port, set.Routing.Upstream.Username)
|
||||
st.upstreamKey = fmt.Sprintf("%s:%d|%s|udp=%t", set.Routing.Upstream.Host, set.Routing.Upstream.Port, set.Routing.Upstream.Username, set.Routing.Upstream.UDP)
|
||||
} else {
|
||||
mark, table := routeResolveIDs(cfg, set)
|
||||
st.mark = mark
|
||||
|
|
|
|||
|
|
@ -818,6 +818,27 @@ func TestRoutingLearnHost(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestBuildRouteStateTracksUpstreamUDP(t *testing.T) {
|
||||
cfg := config.NewConfig()
|
||||
|
||||
newSet := func(udp bool) *config.SetConfig {
|
||||
s := &config.SetConfig{Id: "s1"}
|
||||
s.Routing.Enabled = true
|
||||
s.Routing.Mode = config.RoutingModeProxy
|
||||
s.Routing.Upstream.Host = "192.168.1.1"
|
||||
s.Routing.Upstream.Port = 8480
|
||||
s.Routing.Upstream.UDP = udp
|
||||
return s
|
||||
}
|
||||
|
||||
withUDP := buildRouteState(&cfg, newSet(true))
|
||||
withoutUDP := buildRouteState(&cfg, newSet(false))
|
||||
|
||||
if routeStateEqual(withUDP, withoutUDP) {
|
||||
t.Error("toggling upstream.udp must change the rule state, otherwise the UDP tproxy rules are never rebuilt and keep diverting to a port with no listener")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteAddResolvedIPs(t *testing.T) {
|
||||
origCache := routeRuleCache
|
||||
origEngine := routeEngine
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue