Bug 218508 - Tunneling and aliases using the tun device, reusing a destination address works with IPv4, but not IPv6
Summary: Tunneling and aliases using the tun device, reusing a destination address wor...
Status: New
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 11.0-RELEASE
Hardware: Any Any
: --- Affects Some People
Assignee: freebsd-net (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-04-09 08:29 UTC by Christian Sturm
Modified: 2021-09-29 08:09 UTC (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Christian Sturm 2017-04-09 08:29:27 UTC
I can create a tun device by using:

ifconfig tun0 create
ifconfig tun 10.1.2.0 196.2.3.4 
ifconfig tun alias 196.2.3.3 196.2.3.4
ifconfig up

This works great and allows me to use the 10.1.2.0 and have it tunneled over the second (aliased) tunnel when I set up the correct route using /sbin/route add -net 10.0.0.0/8 -iface tun0.

I have this setup, using IPv6 only working as it is supposed to. To explain what I am am actually achieving with this: In the background I am using BGP over a one-to-one VPN software using the tunnel interface.

Doing the same thing on IPv6, however doesn't work when I add an alias, using the same destination address twice:

ifconfig tun0 inet6 fdaa:abc:abc:abc::1 fe80::111:0 prefixlen /128
ifconfig tun0 inet6 alias fe80::111:1 fe80::111:0 prefixlen /128
ifconfig: ioctl (SIOCAIFADDR): File exists

Using another destination address this works however. Since this works on IPv4 I'd expect it to work here as well.
Comment 1 dmilith 2020-07-30 13:40:19 UTC
I have same bug on 12.1… "file exists":

```
socket(PF_INET,SOCK_DGRAM|SOCK_CLOEXEC,0)        = 3 (0x3)
ioctl(3,SIOCGIFINDEX,0x7fffffff9f20)             = 0 (0x0)
close(3)                                         = 0 (0x0)
__sysctl(0x7fffffff9f40,0x6,0x0,0x7fffffff9f38,0x0,0x0) = 0 (0x0)
__sysctl(0x7fffffff9f40,0x6,0x80161e000,0x7fffffff9f38,0x0,0x0) = 0 (0x0)
socket(PF_INET6,SOCK_DGRAM,0)                    = 3 (0x3)
ioctl(3,SIOCDIFADDR_IN6,0x1054278)               ERR#49 'Can't assign requested address'
ioctl(3,SIOCAIFADDR_IN6,0x1054278)               ERR#17 'File exists'
ifconfig: write(2,"ifconfig: ",10)                       = 10 (0xa)
ioctl (SIOCAIFADDR)write(2,"ioctl (SIOCAIFADDR)",19)             = 19 (0x13)
: write(2,": ",2)                                        = 2 (0x2)
File exists
write(2,"File exists\n",12)                      = 12 (0xc)
exit(0x1)
process exit, rval = 1
```
Comment 2 Denton Gentry 2021-02-25 15:39:43 UTC
We've been encountering this in https://github.com/tailscale/tailscale/issues/1307

The problem occurs because rtrequest1_fib returns EEXIST.

root@freebsd12:~ # dtrace -n 'fbt:::return /(int)arg1 == EEXIST / { stack(); }' -c "ifconfig tun0 inet6 fd7a:115c:a1e0:ab12:4843:cd96:625c:1f1b fd7a:115c:a1e0:ab12:4843:cd96:625c:1f1b prefixlen 128"
dtrace: description 'fbt:::return ' matched 29364 probes
ifconfig: ioctl (SIOCAIFADDR): File exists
dtrace: pid 1175 exited with status 1
CPU     ID                    FUNCTION:NAME
  1  52657            rtrequest1_fib:return
              kernel`rtinit+0x421
              kernel`in6_update_ifa+0xc6b
              kernel`in6_control+0x96f
              kernel`ifioctl+0x47f
              kernel`kern_ioctl+0x2b7
              kernel`sys_ioctl+0xfa
              kernel`amd64_syscall+0x387
              kernel`0xffffffff8106785e

That happens because rn_addroute returns NULL:

rn_addroute(void *v_arg, void *n_arg, struct radix_head *head,
    struct radix_node treenodes[2])
...
        /*
         * Deal with duplicated keys: attach node to previous instance
         */
        saved_tt = tt = rn_insert(v, head, &keyduplicated, treenodes);
        if (keyduplicated) {
                for (t = tt; tt; t = tt, tt = tt->rn_dupedkey) {
#ifdef RADIX_MPATH
                        /* permit multipath, if enabled for the family */
                        ...
#endif
                        if (tt->rn_mask == netmask)
                                return (0);


There is a bunch of code within the #ifdef RADIX_MPATH block above. Enabling RADIX_MPATH in the kernel config does allow the tun0 address assignment to succeed, though I haven't fully tested if it works as expected.


It looks like this behavior was understood, in a comment in sys/netinet6/in6.c:in6_notify_ifa from 2004:

        /*
         * If a new destination address is specified for a point-to-point
         * interface, install a route to the destination as an interface
         * direct route.
         * XXX: the logic below rejects assigning multiple addresses on a p2p.
         * interface that share the same destination.
         */
        plen = in6_mask2len(&ia->ia_prefixmask.sin6_addr, NULL); /* XXX */
        if (!(ia->ia_flags & IFA_ROUTE) && plen == 128 &&
            ia->ia_dstaddr.sin6_family == AF_INET6) {
Comment 3 Zhenlei Huang freebsd_committer freebsd_triage 2021-08-06 10:34:41 UTC
(In reply to Christian Sturm from comment #0)
The IPv6 stack does not behave the same as IPv4 stack.

In this case, you can add an IPv6 alias without the destination address to tun0.

```
ifconfig tun0 inet6 2001:db8:: 2001:db8::1 prefixlen 128
ifconfig tun0 inet6 alias 2001:db8::2 prefixlen 128
```

The long answer:
In principle, a tunnel interface can be unnumbered. For a router, you can "borrow" the global unique address on loopback interface as the local address.

As for numbered tunnel interface, is the peer should be numbered? No, at least in principle not required.

We give another thought on the remote address of tunnel interface, if both ends are numbered, then should either end has only exactly one IP address? No.

Due to historical reason, the destination address of tunnel interface can not be omitted of the FreeBSD IPv4 stack implementation. But it is not the case of IPv6 stack.

Still we can teach the FreeBSD kernel to "smartly" process IPv6 aliases with same destination address.