[Swan-dev] leftsubnet's virtual specifier [long!]

D. Hugh Redelmeier hugh at mimosa.com
Sun May 26 19:02:41 UTC 2019

First, let me admit that I don't understand this stuff.  So when I say 
something, there's a good chance that I've got it wrong.

Here is what ipsec.conf(5) says:

[in a conn]
    private subnet behind the left participant, expressed as
    network/netmask (actually, any form acceptable to
    ipsec_ttosubnet(3)); Currently, IPv4 and IPv6 ranges are
    supported. if omitted, essentially assumed to be left/32,
    signifying that the left end of the connection goes to the
    left participant only

    It supports two magic shorthands vhost: and vnet:, which can
    list subnets in the same syntax as virtual-private. The value
    %priv expands to the networks specified in virtual-private.
    The value %no means no subnet. A common use for allowing
    roadwarriors to come in on public IPs or via accepted NATed
    networks from RFC1918 is to use leftsubnet=vhost:%no,%priv.
    The vnet: option can be used to allow RFC1918 subnets without
    hardcoding them. When using vnet the connection will
    instantiate, allowing for multiple tunnels with different

    a boolean (yes/no) that determines, when *subnet=vhost: is
    used, if the virtual IP claimed by this states created from
    this connection can with states created from other

    Note that connection instances created by the Opportunistic
    Encryption or PKIX (x.509) instantiation system are distinct
    internally. They will inherit this policy bit.

    The default is no.

    This feature is only available with kernel drivers that
    support SAs to overlapping conns. At present only the (klips)
    mast protocol stack supports this feature.

[in config setup]
    contains the networks that are allowed as subnet= for the
    remote clients when using the vhost: or vnet: keywords in the
    subnet= parameters. In other words, the address ranges that
    may live behind a NAT router through which a client connects.
    This value is usually set to all the RFC-1918 address space,
    excluding the space used in the local subnet behind the NAT
    (An IP address cannot live at two places at once). IPv4
    address ranges are denoted as %v4:a.b.c.d/mm and IPv6 is
    denoted as %v6:aaaa::bbbb:cccc:dddd:eeee/mm. One can exclude
    subnets by using the !. For example, if the VPN server is
    giving access to, this option should be set
    This parameter is only needed on the server side and not on
    the client side that resides behind the NAT router, as the
    client will just use its IP address for the inner IP setting.
    This parameter may eventually become per-connection. See also

    Note: It seems that T-Mobile in the US and Rogers/Fido in
    Canada have started using as their pre-NAT range.
    This range technically belongs to the Defence Interoperable
    Network Services Authority (DINSA), an agency of the Ministry
    of Defence of the United Kingdom. The network range seems to
    not have been announced for decades, which is probably why
    these organisations "borrowed" this range. To support
    roadwarriors on these 3G networks, you might have to add it
    to the virtual-private= line.

[in BUGS]
Multiple L2TP clients behind the same NAT router, and multiple
L2TP clients behind different NAT routers using the same Virtual
IP is currently only working for the KLIPSNG stack.


The description of leftusbnet= should be broken down into more
paragraphs.  The ideas need to be separated better.


>From the description of leftsubnet=

    The vnet: option can be used to allow RFC1918 subnets without
    hardcoding them.

Surely this describes %priv, not vnet:.  And only if vitual-private=
hasn't overridden this setting.

This error is repeated in the description of virtual-private=.


The description of virtual-private= does not say that it defaults to
the RFC1918 subnets.  But it is kind of suggested.  Is this the


>From the description of overlapip=

    a boolean (yes/no) that determines, when *subnet=vhost: is
    used, if the virtual IP claimed by this states created from
    this connection can with states created from other

I cannot parse this English.

I think that "virtual IP" should be "virtual subnet".

I suspect that "states" is the wrong word in both instances.

I think that the word overlap is missing.


in the description of virtual-private=, the example is too wide for a
man page.

The note should perhaps be moved to BUGS.


check_virtual_net_allowed() is only called in IKEv1 code.  Does that
mean that anything goes in IKEv2?


The true syntax of virtual-private= allows the %v4: and %v6: prefixes
to be dropped.  In that case, the IP version is inferred from the


Contrary to the documentation, the list of subnets in the virtual
specification of leftsubnet= is not the same as in virtual-private=.
The latter allows "!" (to denote exclusion) and the former does not.


The implemented syntax of leftsubnet= virtual options is:

	{vhost,vnet}:[methods [, method]* ]

where method is any of:

	%no  (meaning no virtual IP (accept IP of host/32))
	%priv  (meaning accept system-wide private net list)
	[%v4:]x  (meaning accept literal IPv4 subnet x)
	[%v6]:x  (meaning accept literal IPv6 subnet x)
	%all  (meaning accept all IPs [only for testing])

These methods can be combined arbitrarily, but perhaps not
meaningfully.  The code in check_virtual_net combines them in ways
that might well be accidental.

Should they be combine as an "or"/"union" or an "and"/"intersection"?

Obviously, all uses of literal IP subnets are united: any is accepted.

All uses of %no in our tree seem to be vhost:%priv,%no.  Should all
other uses be prohibited?  Should it be simplified to a single token?
That should make the description more clear.

%all should perhaps override everything else.  Maybe it should be
considered a bug to combine it.

%priv, %v4, and %v6 could make sense to combine with union or
intersection.  Currently it is union.

What the code of check_virtual_net_allowed does, in this order:

- if vhost: if proposed subnet is not a /32: fail

- if %no: if proposed subnet is a /32 and it contains (is) host
  address, succeed

- if %priv and is included in the good list of virtual-private and
  isn't included in the bad list, succeed.  Otherwise, remember
  tentative failure.

- if there was a list of subnets in the virtual part, and the proposed
  was in it, succeed.  Otherwise, remember tentative failure

- if %all, succeed

- if there was a tentative failure, fail

- succeed.

That's complicated to comprehend!

I think that %no should fail if it doesn't succeed.  In other words,
it should constrain the proposed subnet to exactly the host, with no subsequent
escape clauses.  In other words, I'd change its rule to:

- if %no: if proposed subnet is not a /32 or it does not contain
  (isn't) host address, fail

This is kind of an "and"/"intersection" instead of the current

%all overrides %priv and any explicit list of IPs.  Perhaps it should
not be allowed to appear with them.

More information about the Swan-dev mailing list