Short version:

I'm trying to set up a "road warrior"-style VPN like the one described
at https://www.openbsd.org/faq/faq17.html but I'm trying to use IPv6 so
I can have globally-routable addresses (so I'm not using NAT). So far
I've gotten the initiator and the responder to set up a security
association (to be precise, according to "ipsecctl -sa" the
initiator/client sees one SA and zero flows, and the responder/server
sees two SA's and two flows). When I try to ping the client's virtual
IP address from the server, tcpdump running on the egress interfaces of
both the client and the server see appropriately-sized "esp" packets
going from the server to the client as expected, but tcpdump doesn't
see any traffic emerging on the "lo1" loopback interface the FAQ told
me to create, nor does it see any evidence of ping-responses being
sent. When I try to ping from the client to the server, I get
"ping6: sendmsg: Permission denied".


Longer version:

The client is running -current and the server is running 7.3.

The server is a VPS hosted in a datacenter somewhere, and my VPS
provider gives me a whole /64 subnet to work with. My VPS provider
doesn't appear to look at neighbor discovery traffic; it routes all
traffic in that /64 to my VirtIO interface, regardless of which IP
address it is, which seems like a handy feature for what I'm trying to
do.

Here is what I want to do: the client should open an IPsec tunnel to
the server. The client should request an IPv6 address from the server's
enormous /64 subnet. Then, applications running on the client should
have the option to use that "virtual" IPv6 address as though it was
available on a local interface on the client machine. The client would
effectively be multihosted: in addition to its regular physical
Internet connection it would also have this IPsec tunnel that also
connects to the global Internet. My plan right now is to use rdomains
to control which applications use the tunnel and which ones use the
regular egress interface, but I'm not attached to that specific
approach.

The application should be obvious: I'm trying to set up a region-faking
VPN, so that I can have applications appear to connect to the Internet
from different continents.

Both client and server are using the same pf.conf that is installed by
default. I tried adding "pass on enc0" to the client's pf.conf and
"pass on enc0 tagged ROADW" to the server's, but it hasn't made a
difference because (I assume) my existing pf.conf rules weren't
blocking much to begin with.

Here are my config files, with IP addresses and server names changed.
But basically you should see the client at 2001:db8:1::1 connecting to
the server at 2001:db8:2::1 and requesting a random IP address in the
server's subnet, e.g. 2001:db8:2::485b:4ac7. I have also put the
appropriate keys in /etc/iked/pubkeys/fqdn/.

=== Client-side configuration ===
$ cat /etc/hostname.lo1
rdomain 1

$ cat /etc/iked.conf
ikev2 'client_config' active esp \
        rdomain 1 \
        from dynamic to any \
        local 2001:db8:1::1 \
        peer  2001:db8:2::1 \
        srcid client.example.org \
        dstid server.example.org \
        request address any \
        iface lo1

=== Server-side configuration ===

$ cat /etc/sysctl.conf
net.inet6.ip6.forwarding=1

$ cat /etc/iked.conf
ikev2 'server_config' passive esp \
        from any to dynamic \
        local 2001:db8:2::1 \
        peer  2001:db8:1::1 \
        srcid server.example.org \
        config address 2001:db8:2::/64 \
        tag "ROADW"

=== End of configurations ===

Does it work? Sort of. Here's what I see on the server:

=== Checking status on the server ===

# ipsecctl -sa
FLOWS:
flow esp in from 2001:db8:2::485b:4ac7 to ::/0 peer 2001:db8:1::1 srcid 
FQDN/server.example.org dstid FQDN/client.example.org type require
flow esp out from ::/0 to 2001:db8:2::485b:4ac7 peer 2001:db8:1::1 srcid 
FQDN/server.example.org dstid FQDN/client.example.org type require

SAD:
esp tunnel from 2001:db8:2::1 to 2001:db8:1::1 spi 0x001a3754 enc aes-128-gcm
esp tunnel from 2001:db8:1::1 to 2001:cb8:2::1 spi 0x7634a3b6 enc aes-128-gcm

$ route show
Internet6:
Destination             Gateway         Flags   Refs      Use   Mtu  Prio Iface
default                 fe80::1234%vio0 UGS        8      578     -     8 vio0 
::/96                   localhost       UGRS       0        0 32768     8 lo0  
localhost               localhost       UHhl      10       90 32768     1 lo0  
::ffff:0.0.0.0/96       localhost       UGRS       0        0 32768     8 lo0  
2001:db8:2::/64         server          UCn        3     3630     -     4 vio0 
2001:db8:2::485b:4ac7   link#1          UHLc       0     3633     -     3 vio0 
...and a whole lot more

=== Checking status on the client ===

# ipsecctl -sa
FLOWS:
No flows

SAD:
esp tunnel from 2001:db8:2::1 to 2600:db8:1::1 spi 0x001a3754 enc aes-128-gcm

$ ifconfig lo1
lo1: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> rdomain 1 mtu 32768
        index 4 priority 0 llprio 3
        groups: lo
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo1 prefixlen 64 scopeid 0x4
        inet6 2001:db8:2::485b:4ac7 prefixlen 128

$ route -T1 show
Routing tables

Internet6:
Destination             Gateway                 Flags Refs Use  Mtu Prio Iface
default                 2001:db8:2::485b:4ac7   UGS   0     1  32768  6  lo1
localhost               localhost               UHl   0    37  32768  1  lo1
2001:db8:2::485b:4ac7   2001:db8:2::485b:4ac7   UHhl  1     2  32768  1  lo1
fe80::1%lo1             fe80::1%lo1             UHl   0  2893  32768  1  lo1
ff01::%lo1/32           fe80::1%lo1             Um    0    12  32768  4  lo1
ff02::%lo1/32           fe80::1%lo1             Um    0    12  32768  4  lo1

[This is the complete routing table; I didn't delete any rows.]

====

We see that the server sees two security associations (one in each
direction) and two flows (also one in each direction), but the client
sees only one security association (in the server-to-client direction)
and no flows.

I trimmed a lot of irrelevant stuff out of the server's routing table
but it looks correct. The server recognizes itself as the gateway for
the entire 2001:db8:2::/64 subnet (though this has nothing to do with
the IPsec tunnel, and is just a statement that traffic on the local
subnet doesn't need to go through a router, and thus does not require
an external gateway). It also recognizes that traffic to the client's
virtual IP should be sent over "link#1", which I assume is the tunnel.

Meanwhile, the client's routing table seems wrong. The client
recognizes that it owns its own virtual IP address, and it recognizes
itself as its own default gateway. But it doesn't have any references
to "link#n" for any n. That perhaps makes sense bcause there is no
outward flow to route packets to.

We get three interesting results from pings:

=== Test #1 ===

If I run "ping6 2001:db8:2::485b:4ac7" from the server and watch the
egress interfaces on both the client and server and filter on "esp",
I see appropriately-sized packets with "spi 0x001a3754". (If I pass
the "-s" option to ping6, the sizes of the packets change.) If I run
"netstat -s -pesp" on both the client and the server, I see that the
server thinks it has output some number of ESP packets and received
none, and the client htinks it has received the same number of ESP
packets but sent none. So, OK, clearly we have a one-sided ping.

But when I run "tcpdump -ilo1" on the client I get nothing! The
encrypted ESP packets are coming in, and iked.conf says they're
supposed to emerge on lo1 (which ifconfig confirms exists and has
the expected automatically-assigned IP address) but the decrypted
ICMP packets are nowhere to be seen.

I get the same behavior if I run "nc -u 2001:db8:2::485b:4ac7 5000"
from the server---the only differences between this and ping6 are
that I'm not *expecting* any replies, but also I'm sending UDP
packets instead of ICMPv6 packets so it seems like they'd be more
likely to show up on the lo1 interface. But they don't. (I also
tried "tcpdump -ienc0" on the client with both tests but the packets
never show up anywhere.)

=== Test #2 ===

The client can't ping the server at all!

$ route -T1 exec ping6 2001:db8:2::1
PING 2001:db8:2::1 (2001:db8:2::1): 56 data bytes
ping6: sendmsg: Permission denied
ping: wrote mudge 64 chars, ret=-1
ping6: sendmsg: Permission denied
ping: wrote mudge 64 chars, ret=-1

Curiously, I get the same results if I try to have the client ping
itself at 2001:db8:2::485b:4ac7, but the client *can* ping itself
at both ::1 and fe80::1%lo1 from routing domain 1.

How in the world am I getting "Permission denied" when I ping
certain addresses? What does that even mean?

=== Test #3 ===

I know I'm getting ahead of myself, if I want this connection to be
usable for actual connections, the server is going to have to be able
to route traffic from *other* servers to the client. I happen to have
a second server handy, so I do the following:

On the original server:
$ tcpdump -l -ivio0 icmp6 | grep 4ac7

On the extra server:
$ ping6 2001:db8:2::485b:4ac7

What I see on tcpdump is that the original server is spamming its local
link with neighbor solicitation requests asking who has
2001:db8:2::485b:4ac7. That behavior strikes me as a little weird. When
the server originates a packet that is destined for the VPN IP address,
it correctly sends it over the IPsec tunnel (even though the reciever
of that packet appears to drop it on the floor). But when the server
receives a packet from someone else that is destined for that same VPN
IP address, the server forgets that this route exists and instead
thinks, "Hey, I recognize that subnet---I'll bet this address belongs
to someone else on the local link." Why the discrepancy? The server has
only one routing domain, so I would expect the same routing rules to
apply to both.


=== In summary ===

For all this mess of detail, I think I have only two problems:

1. Why does my client have only one security association and zero
   flows? I suspect that this is the cause of all of my other issues
   with the tunnel.

2. Why does the server route packets over the tunnel only when they
   originate on the server itself? (The ip6.forwarding sysctl is
   enabled---the problem is that it wants to route these packets
   onto the local link, not that it refuses to route at all.)


Does anybody have any thoughts on what I'm doing wrong?

Thanks,
Anthony Coulter

Reply via email to