Project

General

Profile

Bug #70

IPv6

Added by Martin Willi over 11 years ago. Updated over 11 years ago.

Status:
Closed
Priority:
High
Category:
testing
Target version:
Start date:
Due date:
Estimated time:
Affected version:
5.9.0
Resolution:

Description

Hi,

I recently started playing around with IPSec, as I would like to establish some
sort of site-vpn providing priviledged access to a server in the internet for my
local machines. I am quite experienced using OpenVPN, but since both server and
local machines have IPv6 access, I thought IPSec could be the more adequate
solution. :)

My setup is basically the one for roadwarriors, with some unconventional virtual
IP. This is because the server has a /64 subnet assigned to it, but its not
routed, I can just choose an arbitrary number of them and assign them to my
single external interface, and the next hop checks for link-local availability
of the IP prior to sending any packets to it. My idea is to use unused IPs from
that subnet to assign them to roadwarriors, so I can match on the subnet in
certain services' configs (e.g. $mynetworks in postfix).

My local testing machine is dual stacked, IPv6 access through a sixxs tunnel.

So for later illustration, let's assume the nodes have the following IPs (yes,
they're invalid, but should improve readability):

Server: 2001:ser:ver::/64, of which 2001:ser:ver::1 is assigned to the external
    interface.

Client: 2001:cli:ent::2/48, local sixxs peer-to-peer address
    2001:ser:ver::40/64, virtual IP to be assigned by strongswan

In fact, both machines are dual-stacked. But since I don't want to use any IPv4
in my setup, I'll just omit them. As you later will see, the existing IPv4
default routes will cause problems, but this is only due to them existing, and
not related to their actual value.

I started using some very minimalistic setup:

-------------- Begin Server ipsec.conf -----------------
config setup
    # nothing

conn madmax
    left=2001:ser:ver::1
    leftcert=server.crt
    right=%any
    auto=add
-------------- End Server ipsec.conf -------------------
-------------- Begin Client ipsec.conf -----------------
config setup
    # nothing

conn madmax
    left=%any
    leftsourceip=2001:ser:ver::40
    leftcert=client.crt
    right=2001:ser:ver::1
    rightid="OU=ipsec, CN=server" 
    auto=start
-------------- End Client ipsec.conf -------------------

IPSec comes up fine, but on client side I get the following error:

plutor31264: address family inconsistency in connection

I know, I found %any6 shortly after (by reading the code, BTW), but the address
family to use for the connection should be clear as an IPv6 address has been
specified for the remote side. So I think having %any and %any6 does not make
sense at all, since I could have used a domain name instead of the remote IPv6
IP which makes me unable to tell which address family has to be used for the
local side, since this depends on whether IPv6 is currently available or not,
and to which IP the domain name resolves.

But well, I ought to get this running, so I changed %any on both server and
client to %any6. This leads to another error:

Client:

plutor32316: Using Linux 2.6 IPsec interface code
[...]
plutor32316: "madmax": we have no ipsecN interface for either end of this connection

So although it uses the kernel IPSec implementation, pluto tries to use some
virtual NIC. For me this was Game Over with IKEv1 here, as I really want to use
the kernel implementation and pluto seems not to.

In order to use IKEv2, I added 'keyexchange=ikev2' to the client's config, and
restarted the client side daemons. As expected, I'm now receiving logs from
charon instead of pluto, but also not really good news (did some reformatting to
break long lines):

Server:

charon: 11[IKE] peer requested virtual IP 2001:ser:ver::40
charon: 11[IKE] no virtual IP found, sending INTERNAL_ADDRESS_FAILURE
charon: 11[AUD] traffic selectors 2001:ser:ver::1/128 === 0.0.0.0/0  inacceptable
charon: 11[ENC] generating IKE_AUTH response 1 [ IDr CERT AUTH N(AUTH_LFT) N(MOBIKE_SUP)
        N(ADD_4_ADDR) N(ADD_4_ADDR) N(INT_ADDR_FAIL) N(TS_UNACCEPT) ]
Client:

charon: 11[IKE] installing new virtual IP 2001:ser:ver::40
charon: 11[AUD] received INTERNAL_ADDRESS_FAILURE notify, no CHILD_SA built
charon: 11[AUD] received INTERNAL_ADDRESS_FAILURE notify, no CHILD_SA built

As expected, the virtual IP has been added to the client's default route
interface (i.e. sixxs), but no policies have been installed (I use 'setkey' from
ipsec-tools to verify). The first error on server-side is in fact a minor one. I
still could not find out what there has to be done to make charon successfully
verify the IP's availability, but adding 'rightsourceip=%config' to the server's
ipsec.conf makes the error go away (at least):

Server:

charon: 11[IKE] peer requested virtual IP 2001:ser:ver::40
charon: 11[IKE] assigning virtual IP 2001:ser:ver::40 to peer
charon: 11[AUD] traffic selectors 2001:ser:ver::1/128 === 0.0.0.0/0  inacceptable
charon: 11[ENC] generating IKE_AUTH response 1 [ IDr CERT AUTH CP N(AUTH_LFT)
        N(MOBIKE_SUP) N(ADD_4_ADDR) N(ADD_4_ADDR) N(TS_UNACCEPT) ]

To dig deeper, I enabled level 3 debugging for 'cfg', I guess these are the
relevant parts:

Server:

charon: 11[CFG] selecting traffic selectors for us:
charon: 11[CFG]  config: 2001:ser:ver::1/128, received: 2001:ser:ver::1/128
         => match: 2001:ser:ver::1/128
charon: 11[CFG] selecting traffic selectors for other:
charon: 11[CFG]  config: 2001:cli:ent::2/128, received: 0.0.0.0/0 => no match
Client:

charon: 10[CFG] proposing traffic selectors for us:
charon: 10[CFG]  dynamic/32 (derived from dynamic/32)
charon: 10[CFG] proposing traffic selectors for other:
charon: 10[CFG]  2001:ser:ver::1/128 (derived from dynamic/32)

So it seems like something goes wrong on the receiving side, when determining
the remote peer address in a config using right=%any6 (where right is remote).

I've tried with statically defined IP addresses on both sides for everything, in
order to eliminate the problem. So the configs now look like this:

-------------- Begin Server ipsec.conf -----------------
config setup
    # nothing

conn madmax
    left=2001:ser:ver::1
    right=2001:cli:ent::2
    leftcert=server.crt
    leftsourceip=2001:ser:ver::1
    rightsourceip=2001:ser:ver::40
    leftid="OU=ipsec, CN=server" 
    rightid="OU=ipsec, CN=client" 
    auto=add
-------------- End Server ipsec.conf -------------------
-------------- Begin Client ipsec.conf -----------------
config setup
    # nothing

conn madmax
    keyexchange=ikev2
    left=2001:cli:ent::2
    right=2001:ser:ver::1
    leftcert=client.crt
    leftsourceip=2001:ser:ver::40
    rightsourceip=2001:ser:ver::1
    leftid="OU=ipsec, CN=client" 
    rightid="OU=ipsec, CN=server" 
    auto=start
-------------- End Client ipsec.conf -------------------

But still the same error. For curiosity, I've switched back to IKEv1, using the
same configs as above. And surprisingly:

Client:

plutor12017: "madmax" #2: sent QI2, IPsec SA established {ESP=>0x2938d2fe <0x11292921}
Server:

plutor3445: "madmax" #2: IPsec SA established {ESP=>0xf48cc2d9 <0xeae943a0}

Also the policies are installed on both sides, but the virtual IP is not being
assigned to any interface. After adding 2001:ser:ver::40/64 to the default
route's interface, everything works fine. But please don't dare interpreting
this as a "found the bug myself" resolution. As you may remember, I started with
a setup for multiple roadwarriors with a single conn-definition on the server,
and ended now with some completely static setup which needs maintenance on the
server, more maintenance on the client and additionally some user interaction
after starting the tunnel on the client to add the virtual IP. So you may agree
that this is about as far away from what I wanted as it gets.

Please consider this as a general bug report about IPv6 support. My setup might
be a bit uncommon, but I bet it's trivial to reproduce the problems I've
encountered.

I would really like to help fixing IPv6 compatibility, but there are a few
things holding me back:
  • I could not find the path to the subversion repository. Yes, I could use a
    snapshot or maybe even the current version's sources to fix stuff, but
    repositories are so much better for this stuff. Please add a link to the page,
    like you have for CVS in the 2.X branch.
  • Well, debugging code which consists of 80% function pointer calls is not
    really trivial if one has no idea, so maybe one of you could point me to the
    right direction regarding this "inacceptable traffic selectors" problem with
    charon.

Another thing I've noticed when switching to IKEv2: when using ip6tables with a
drop-policy, UDP fragments are discarded by default (as the second to last
fragment contains no UDP header, and ip6tables therefore can't match on the
destination port). Using the following eliminated this problem:

| ip6tables -A INPUT -m frag -j ACCEPT

but that's probably not what one wants, so I'm currently playing with this:

| ip6tables -A INPUT -p udp --dport 4500 -m frag --fragfirst -j CONNMARK --set-mark 0x42
| ip6tables -A INPUT -p udp --dport 4500 -j ACCEPT
| [...]
| ip6tables -A INPUT -m frag -m connmark --mark 0x42 -j ACCEPT

So before accepting packets to port 4500, I check for them being the first
fragment in a row, and mark the connection with 0x42, a reasonable value. After
everything has failed for any packet, I lookup the mark value of it's connection
and accept if it has the value from above. Not sure if this is all right, but it
seems to work for now.

Greetings, Phil

History

#1 Updated by Andreas Steffen over 11 years ago

  • Status changed from New to Closed
  • Affected version set to fixed

Changeset r4856 introduces full support for dynamic/128 which fixes the "inacceptable traffic selectors" problem for IKEv2. Also "%any6" can now be used, although %any works as well. IPv6 virtual IP addresses can now be assigned by a server either statically or out of a pool and are installed on the client side as an interface. Unfortunately the Linux 2.6 kernel does not allow the creation of IPv6 source routes, so that the IP packets going into the ESP tunnel do not assume the virtual IP as source address.

#2 Updated by Martin Willi over 11 years ago

  • Status changed from Closed to Feedback
  • Affected version deleted (fixed)

Hi,

Replying to [comment:1 andreas]:

Changeset r4856 introduces full support for dynamic/128 which fixes the
"inacceptable traffic selectors" problem for IKEv2. Also "%any6" can now be
used, although %any works as well. IPv6 virtual IP addresses can now be
assigned by a server either statically or out of a pool and are installed on
the client side as an interface.

Great, a daily snapshot dedicated to me. :)
I've already done some (positive) tests, but see below.

Unfortunately the Linux 2.6 kernel does not
allow the creation of IPv6 source routes, so that the IP packets going into
the ESP tunnel do not assume the virtual IP as source address.

Well, this occurs to not be a problem. In my setup, the virtual IP is being
assigned to the default route's interface on client side, with a /128 netmask.
So this should tell the kernel no more than that it's the final destination for
that IP. And yet, when addressing my virtual endpoint (which in my case is the
same IP as the real IPsec endpoint), the virtual IP is being used as source
address, so the packets pass through the tunnel as they should. (Guess this has
something to do with this xfrm thing, I don't understand yet.)

So (for the record) I've got an IPv6-only roadwarrior setup up and running using
the following configs:

---------------- Begin Client ipsec.conf --------------------
config setup
    # nothing

conn madmax
    keyexchange=ikev2
    left=%any6
    right=2001:ser:ver::1
    leftsourceip=2001:ser:ver::40
    leftcert=client.crt
    rightid="OU=ipsec, CN=server" 
    leftid="OU=ipsec, CN=client" 
    auto=start
---------------- End Client ipsec.conf ----------------------
---------------- Begin Server ipsec.conf --------------------
config setup
    # nothing

conn madmax
    left=2001:ser:ver::1
    leftcert=server.crt
    right=%any
    rightsourceip=%config
    leftid="OU=ipsec, CN=server" 
    auto=add
---------------- End Server ipsec.conf ----------------------

Which, as you see, is fully dynamic again. But (and this is why I'm reopening
this ticket) when playing around to see how waterproof everything is, I noticed
some problems which in future could lead to problems when using strongswan in a
dual-stacked environment (i.e. when the underlying IP protocol matters less or
intentionally should be chosen dynamically):

  • Changing %any6 to %any on client-side brings back the traffic selectors problem:
    charon: 10[IKE] 2001:cli:ent::2 is initiating an IKE_SA
    [...]
    charon: 11[IKE] peer requested virtual IP 2001:ser:ver::40
    charon: 11[IKE] assigning virtual IP 2001:ser:ver::40 to peer
    charon: 11[IKE] no acceptable traffic selectors found
    

    So despite the connection was opened via IPv6, the client seems to send his IPv4
    address. The address family to use for "translating" the %any statement should
    be made relational to the destination address' family.
  • Using a hostname for the destination definition makes 'left=%any6' useless.
    Although there is an AAAA record for the hostname, the IPv4 address is being
    used, and the local address is chosen apropriately (i.e., the IPv4 one). This
    could be basically the same problem as above, but with different symptoms.

The last point brings up the question about the preferred address family when
using 'left=%any' and 'right=domain.name'. Most applications seem to try IPv6
first and fallback to IPv4, which seems reasonable. This could lead to problems
with strongswan, though: as currently the daemon seems to default to IPv4
(only), having it try IPv6 if available could break existing setups relying on
the IPv4 default.

Greetings, Phil

#3 Updated by Andreas Steffen over 11 years ago

  • Status changed from Feedback to Closed
  • Affected version set to fixed

fixed by Changeset r4860

Also available in: Atom PDF