feat(amneziawg): Phase 2a — IPv6 support + NDP proxy

Adds native dual-stack IPv6 to AmneziaWG inbounds, ported from
coinman-dev/3ax-ui's approach:

- ServerSettings gets ipv6Enabled/ipv6Subnet/ipv6ExternalInterface;
  Instance carries the server's own IPv6 address (first host of the
  subnet) alongside its IPv4 one.
- defaultAmneziaWGClients allocates an IPv6 host address per client
  (second AllowedIPs entry) when the server has IPv6 enabled, reusing
  allocateWireguardAddress — which needed a real fix along the way: it
  always suffixed "/32" regardless of address family, which is wrong
  for an IPv6 host address (needs /128). Now family-aware.
- generateServerConfig's PostUp/PostDown gains IPv6 forward-accept
  rules, proxy_ndp sysctl, and one `ip -6 neigh add/del proxy` entry per
  enabled peer with an IPv6 address — the lightweight per-client
  method, not the ndppd-daemon whole-subnet method (not worth the
  config-file-management complexity at this scale; ndppd itself is
  still installed by install.sh in case that changes later).
- ValidateIPv6Subnet rejects a malformed subnet before save.
- Frontend: ipv6Enabled/ipv6Subnet/ipv6ExternalInterface fields on the
  AmneziaWG inbound form, EN+RU translations, openapi.json/generated/*
  regenerated (the latter via `go run ./tools/openapigen`, pure Go).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Kuzz007
2026-07-25 17:25:58 +03:00
parent a76f0ab233
commit ef13e8567e
15 changed files with 227 additions and 33 deletions
@@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next';
import { Button, Form, Input, InputNumber, Space } from 'antd';
import { Button, Form, Input, InputNumber, Space, Switch } from 'antd';
import { ReloadOutlined } from '@ant-design/icons';
import { FormField } from '@/components/form/rhf';
@@ -47,6 +47,27 @@ export default function AmneziawgFields({ awgPubKey, regenInboundAwg, regenInbou
>
<Input placeholder="eth0" />
</FormField>
<FormField
name={['settings', 'server', 'ipv6Enabled']}
label={t('pages.xray.amneziawg.ipv6Enabled')}
valueProp="checked"
>
<Switch />
</FormField>
<FormField
name={['settings', 'server', 'ipv6Subnet']}
label={t('pages.xray.amneziawg.ipv6Subnet')}
extra={t('pages.xray.amneziawg.ipv6SubnetHint')}
>
<Input placeholder="fd86:ea04:1115::/64" />
</FormField>
<FormField
name={['settings', 'server', 'ipv6ExternalInterface']}
label={t('pages.xray.amneziawg.ipv6ExternalInterface')}
extra={t('pages.xray.amneziawg.ipv6ExternalInterfaceHint')}
>
<Input placeholder="eth0" />
</FormField>
<Form.Item label={t('pages.xray.amneziawg.obfuscation')}>
<Button icon={<ReloadOutlined />} onClick={regenInboundAwgObfuscation}>
{t('pages.xray.amneziawg.regenerateObfuscation')}