radvd-project / radvd

radvd | Official repository: https://github.com/radvd-project/radvd
https://radvd.litech.org/
Other
203 stars 107 forks source link

Make AdvRASrcAddress accept any link local addresses when net.ipv6.ip_nonlocal_bind=1 #163

Open jackyzy823 opened 2 years ago

jackyzy823 commented 2 years ago

current AdvRASrcAddress will only be used when matched one of valid link local addresses.

https://github.com/radvd-project/radvd/blob/b368cb98da5da44154994de573fea24b7c7858fc/device-common.c#L182

I hope that when sysctl net.ipv6.ip_nonlocal_bind=1 is set, AdvRASrcAddress can directly set iface->props.if_addr_rasrc whatever it is a valid link local addresses or not .

Reason: It maybe a rare case . My VPS provider offers an IPv6 Prefix to a machine. I want to set SLAAC in that machine.

when i use normal radvd.conf ,I can get an IPv6 address , but i must set a gateway manually to fe80::1 to make the connection work. (maybe related to https://serverfault.com/a/1069528).
Because the RA message is sent in the just same machine to itself , so it takes self link local address as gateway.

interface eth0
{
        AdvSendAdvert on;
        MinRtrAdvInterval 30;
        MaxRtrAdvInterval 100;
        prefix <PREFIX1>::/64
        {
                AdvOnLink on;
                AdvAutonomous on;
                AdvRouterAddr off;
        };
        RDNSS  <DNS1>
        {
        };
};

Then I found AdvRASrcAddress option , but when i set AdvRASrcAddress { fe80::1 ; }; , radvd refuse to work saying interface eth0 does not exist or is not set up properly, ignoring the interface because of no configured AdvRASrcAddress present, skipping send.

Finally, I tried short circuit the code and set sysctl net.ipv6.ip_nonlocal_bind=1

diff --git a/device-common.c b/device-common.c
index 77a9e77..46d5316 100644
--- a/device-common.c
+++ b/device-common.c
@@ -179,6 +179,8 @@ int setup_iface_addrs(struct Interface *iface)
                        for (struct AdvRASrcAddress *current = iface->AdvRASrcAddressList; current; current = current->next) {
                                for (int i = 0; i < iface->props.addrs_count; i++) {
                                        struct in6_addr cmp_addr = iface->props.if_addrs[i];
+                                       iface->props.if_addr_rasrc = &current->address;
+                                       break;
                                        if (0 == memcmp(&cmp_addr, &current->address, sizeof(struct in6_addr))) {
                                                addrtostr(&(cmp_addr), addr_str, sizeof(addr_str));
                                                dlog(LOG_DEBUG, 4, "AdvRASrcAddress selecting: %s", addr_str);

And it works. I can do SLAAC and the gateway is fe80::1 and connection works.

robbat2 commented 2 years ago

That's a creative solution, but I have a better suggestion, borrowed from HAProxy's transparent option: Foreign sockets IPV6_TRANSPARENT (Linux) / IP_FREEBIND (Linux,FreeBSD?) / IPV6_BINDANY (NetBSD,FreeBSD?) / SO_BINDANY (OpenBSD)

https://github.com/haproxy/haproxy/blob/6dfbef4145845a421e5d9301e26cbc7136d20965/src/sock_inet.c#L246-L267

This would be set as a configuration option, per-interface, and then the kernel should just accept the values set, and permit it on a per-interface basis, which is safer than the sysctl, and more portable.