Bug 51620

Summary: Ошибка обновления dns записей при наличии нескольких ip адресов на одном интерфейсе
Product: Sisyphus Reporter: Nikolai Zurabishvili <zurabishvilinn>
Component: samba-winbind-dnsupdateAssignee: Evgenii Sozonov <arzdez>
Status: NEW --- QA Contact: qa-sisyphus
Severity: normal    
Priority: P5 CC: arseny, liannnix, sin, varaksaaa
Version: unstable   
Hardware: x86_64   
OS: Linux   
See Also: https://bugzilla.altlinux.org/show_bug.cgi?id=55600

Description Nikolai Zurabishvili 2024-10-02 15:16:07 MSK
samba-winbind-dnsupdate-0.5-alt2

Стенды (обновлены до сизифа):

Workstation 10.2 x86-64 - клиент
Server 10.2 office x86-64 - dc

Предусловия: Развернут Samba домен, клиенты введены в домен. На клиенте имеется несколько ip адресов на одном интерфейсе:
# ip a
...
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    ...
    inet <ipv4-1> brd <brd-adr> scope global dynamic noprefixroute eth0
       valid_lft 1320sec preferred_lft 1320sec
    inet <ipv4-2> scope global secondary eth0
       valid_lft forever preferred_lft forever
    inet6 <ipv6-1> scope global dynamic mngtmpaddr proto kernel_ra 
       valid_lft 86393sec preferred_lft 14393sec
    inet6 <ipv6-2> scope link proto kernel_ll 
       valid_lft forever preferred_lft forever

Шаги (выполнять на клиенте):
1. # winbind-dnsupdate или # winbind-dnsupdate --disable-dconf -6 (для ipv6) 

Ожидаемый результат: Корректное обновление dns записей для dc

Фактический результат: Ошибка обновление dns записей :

[INFO]: Current IPv4 address: <ipv4-1>
<ipv4-2>
[INFO]: IPv4 address in DNS server: <dc-ipv4>
[INFO]: The IPv4 address of interface eth0 has been changed.
[INFO]: Start the A record update.
[ERROR]: Nsupdate error:
[ERROR]: incorrect section name: <ipv4-2>
syntax error
[ERROR]: A record update failed.

(Для ipv6) :

[INFO]: Current IPv6 address: <ipv6-1>
<ipv6-2>
[INFO]: IPv6 address in DNS server: <dc-ipv6>
[INFO]: The IPv6 address of interface eth0 has been changed.
[INFO]: Start the AAAA record update.
[ERROR]: Nsupdate error:
[ERROR]: incorrect section name: <ipv6-2>
syntax error
[ERROR]: A record update failed.
Comment 1 Arseny Maslennikov 2025-08-14 18:36:53 MSK
https://git.altlinux.org/gears/s/..git?p=samba-winbind-dnsupdate.git;a=blob;f=winbind-dnsupdate;hb=0.7.1-alt1

На строках 585 и 608 глючные функции; они при наличии нескольких адресов на интерфейсе дампят их все через LF. А потом validate_ipv{4,6} ничего по факту не валидируют.
Comment 2 Arseny Maslennikov 2025-08-14 18:37:08 MSK
Далее рекомендации тем, кто будет фиксить:
1) Не надо парсить обычный вывод `ip(8)`. Легко ошибиться, легко словить регрессию. Варианты:
  * ip -br a ...
  * ip -j a ...
Если возьмёте последний, лучше покройте его тестами своими; апстрим iproute2 системно таким не занимается, там бывают регрессии и не все команды умеют выдавать json-данные. Апстрим будет рад, конечно, увидеть патчи.
2) Не надо регулярками парсить текстовое представление адреса, это смерть. IPv6 в этом плане, кстати, проще, чем IPv4. Нужно применить алгоритм inet_pton(3), что на шелле нелегко. Правда, поговорить с ядром по RTNL и получить оттуда адреса в бинарном network byte order виде ещё сложнее.
3) Не надо хавать какие попало адреса.
Понятно, что ряд адресов выпадают из множ-ва тех, которые имеет смысл регистрировать в домене. Этот ряд — подмножество вот этих двух списков:
https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
* Локалхост (::1/128, 127.0.0.0/8)
* IPv4-mapped IPv6 (::ffff:0:0/96)
* Link-local (fe80::/10, 169.254.0.0/16)
* 6to4 (2002::/16)
* ...
Вот это и нужно валидировать. IPv4-mapped и link-local надо обязательно отрывать. Скрыть link-local можно, передав ip(8) фильтр `scope global`.

Кроме того, для IPv6 нужна политика, какие адреса предпочтительно там регистрировать, здесь нужен экспертный совет по домен-решению. Primary/non-temporary или secondary/temporary? Если temporary, то надо брать valid и preferred lifetime в рассмотрение (подозреваю, что и для IPv4 DHCP-клиент их выставляет). В любом случае надо протухшие адреса убирать.
Вот так выглядит результат в неплохом приближении:
  # ip -br -f $family addr show scope global -tentative -deprecated
Но тут всё равно адресов может оказаться несколько, PTR для них всех нужно прописывать. Что приводит нас к пункту 4...
Comment 3 Arseny Maslennikov 2025-08-14 18:38:17 MSK
4) Не должна ли эта программа быть даймоном и реагировать на события, например, от `ip monitor`? Адрес появился (add), у адреса изменились флаги либо другое состояние (он по-change-ился), адрес пропал (delete).
Или не быть даймоном, а запускаться этим даймоном по событию (fork-exec на событие, конечно, но хоть что-то).

Может быть, уже так и есть, а я не в курсе; тогда хорошо.
Comment 4 Artem Varaksa 2025-08-21 10:36:06 MSK
(Ответ для Arseny Maslennikov на комментарий #3)
> 4) Не должна ли эта программа быть даймоном и реагировать на события,
> например, от `ip monitor`? Адрес появился (add), у адреса изменились флаги
> либо другое состояние (он по-change-ился), адрес пропал (delete).
> Или не быть даймоном, а запускаться этим даймоном по событию (fork-exec на
> событие, конечно, но хоть что-то).
> 
> Может быть, уже так и есть, а я не в курсе; тогда хорошо.

В samba-winbind-dnsupdate-0.7.1-alt1.noarch есть .service и .timer:

/usr/lib/systemd/system/winbind-dnsupdate.service
/usr/lib/systemd/system/winbind-dnsupdate.timer


# /usr/lib/systemd/system/winbind-dnsupdate.service
[Unit]
Description=Update dns record service
After=network.target network-online.target

[Service]
Type=oneshot
EnvironmentFile=-/etc/sysconfig/winbind-dnsupdate
ExecStart=/usr/bin/winbind-dnsupdate $OPTIONS

[Install]
WantedBy=multi-user.targe


# /usr/lib/systemd/system/winbind-dnsupdate.timer
[Unit]
Description=Update dns record Daily and on boot

[Timer]
OnBootSec=5min
OnUnitActiveSec=60min

[Install]
WantedBy=timers.target
Comment 5 Arseny Maslennikov 2025-08-21 11:15:53 MSK
(In reply to Artem Varaksa from comment #4)
> (Ответ для Arseny Maslennikov на комментарий #3)
> > 4) Не должна ли эта программа быть даймоном и реагировать на события,
> > например, от `ip monitor`? Адрес появился (add), у адреса изменились флаги
> > либо другое состояние (он по-change-ился), адрес пропал (delete).
> > Или не быть даймоном, а запускаться этим даймоном по событию (fork-exec на
> > событие, конечно, но хоть что-то).
> > 
> > Может быть, уже так и есть, а я не в курсе; тогда хорошо.
> 
> В samba-winbind-dnsupdate-0.7.1-alt1.noarch есть .service и .timer:
> 
> /usr/lib/systemd/system/winbind-dnsupdate.service
> /usr/lib/systemd/system/winbind-dnsupdate.timer
Благодарю за инфо. :)

> # /usr/lib/systemd/system/winbind-dnsupdate.service
> <...>
> 
> # /usr/lib/systemd/system/winbind-dnsupdate.timer
> [Unit]
> Description=Update dns record Daily and on boot
> 
> [Timer]
> OnBootSec=5min
> OnUnitActiveSec=60min
> 
> [Install]
> WantedBy=timers.target
Обновление раз в 60 минут — это, если честно, паллиатив. Оно должно срабатывать по событию изменения множества адресов на интерфейсе (и у главного конкурента, чудаков на букву М, именно так), потому что такое изменение — это преждевременно протухшие записи.

Но просто изменить в таймере промежуток или привязать её к выхлопу `ip monitor addr` — тоже не то, потому что:
— программа, видимо, при работе ещё и выясняет, где сервер, на который слать nsupdate; это несколько DNS-запросов с зависимостью по данным;
— из-за этого слишком часто (в случае флапа адресов на интерфейсе, последовательности мгновенных появлений-пропаданий) её дёргать нельзя, нужен grace period, я бы предложил сделать его сравнимым с neighbour reachability timer, по умолчанию 1,5 секунды.
https://datatracker.ietf.org/doc/html/rfc4861#section-6.3.2, "ReachableTime"