- import of strongswan-2.7.0
- applied patch for charon
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
Routing and Erouting in Pluto
|
||||
=============================
|
||||
|
||||
RCSID $Id: routing.txt,v 1.1 2004/03/15 20:35:29 as Exp $
|
||||
|
||||
This is meant as internal documentation for Pluto. As such, it
|
||||
presumes some understanding of Pluto's code.
|
||||
|
||||
It also describes KLIPS 1 erouting, including details not otherwise
|
||||
documented. KLIPS 1 documentation would be better included in KLIPS.
|
||||
|
||||
Routing and erouting are complicated enough that the Pluto code needs
|
||||
a guide. This document is meant to be that guide.
|
||||
|
||||
|
||||
Mechanisms available to Pluto
|
||||
-----------------------------
|
||||
|
||||
All outbound packets that are to be processed by KLIPS 1 must be
|
||||
routed to an ipsecN network interface. Pluto only uses normal routing
|
||||
(as opposed to "Advanced Routing"), so the selection of packets is
|
||||
made solely on the basis of the destination address. (Since the
|
||||
actual routing commands are in the updown script, they could be
|
||||
changed by the administrator, but Pluto needs to understand what is
|
||||
going on, and it currently assumes normal routing is used.)
|
||||
|
||||
When an outbound packet hits an ipsecN interface, KLIPS figures out
|
||||
how to process it by finding an eroute that applies to the source and
|
||||
destination addresses. Eroutes are global: they are not specific to a
|
||||
particular ipsecN interface (routing needs to get the packets to any
|
||||
ipsecN interface; erouting takes it from there, ignoring issues of
|
||||
source IP address and nexthop (because nobody knows!)). If multiple
|
||||
eroutes apply to the packet, among the ones with the most specific
|
||||
source subnet, the one with the most specific destination subset is
|
||||
chosen (RGB thinks). If no eroute is discovered, KLIPS acts as if it
|
||||
was covered by a DROP eroute (this is the default behaviour; it can be
|
||||
changed). At most one eroute can exist for a particular pair of
|
||||
client subnets.
|
||||
|
||||
There are fundamentally two kinds of eroutes: "shunt" eroutes and ones
|
||||
that specify that a packet is to be processed by a group of IPSEC SAs.
|
||||
Shunt eroutes specify what is to be done with the packet. Remember
|
||||
that these only apply to outbound packets.
|
||||
|
||||
- TRAP: notify Pluto of the packet (presumably to attempt to negotiate
|
||||
an appropriate group of IPSEC SAs). At the same time, KLIPS
|
||||
installs a HOLD shunt (see below) for the specific source and
|
||||
destination addresses from the packet and retains the packet
|
||||
for later reprocessing (KLIPS does not yet implement retention).
|
||||
Beware: if the TRAP's subnets both contained a single IP address
|
||||
then installing the HOLD would actually delete the TRAP.
|
||||
|
||||
- PASS: let the packet through in the clear
|
||||
|
||||
- DROP: discard the packet
|
||||
|
||||
- REJECT: discard the packet and notify the sender
|
||||
|
||||
- HOLD: (automatically created by KLIPS when a TRAP fires) block
|
||||
the packet, but retain it. If there is already a retained
|
||||
packet, drop the old one and retain the new. When the HOLD
|
||||
shunt is deleted or replaced, the retained packet is reinjected --
|
||||
there might now be a tunnel. Note that KLIPS doesn't yet
|
||||
implement the retention part, so HOLD is really like a DROP.
|
||||
|
||||
One consequence of there being only one eroute for a pair of clients
|
||||
is that KLIPS will only use one SA group for output for this pair,
|
||||
even though there could be several SA groups that are authorised and
|
||||
live. Pluto chooses to make this the youngest such group.
|
||||
|
||||
|
||||
|
||||
KLIPS lets through in the clear outbound UDP/500 packets that would
|
||||
otherwise be processed if they originate on this host and meet certain
|
||||
other conditions. The actual test is
|
||||
source == me
|
||||
&& (no_eroute || dest == eroute.dest || isanyaddr(eroute.dest))
|
||||
&& port == UDP/500
|
||||
The idea is that IKE packets between us and a peer should not be
|
||||
sent through an IPSEC tunnel negotiated between us. Furthermore,
|
||||
our shunt eroutes should not apply to our IKE packets (shunt eroutes
|
||||
will generally have an eroute.dest of 0.0.0.0 or its IPv6 equivalent).
|
||||
|
||||
Inbound behaviour is controlled in a quite different way. KLIPS
|
||||
processes only those inbound packets of ESP or AH protocol, with a
|
||||
destination address for this machine's ipsecN interfaces. The
|
||||
processing is as dictated by the SAs involved. Unfortunately, the
|
||||
decapsulated packet's source and destination address are not checked
|
||||
(part of "inbound policy checking").
|
||||
|
||||
To prevent clear packets being accepted, firewall rules must be put in
|
||||
place. This has nothing to do with KLIPS, but is nonetheless in
|
||||
important part of security. It isn't clear what firewalling makes
|
||||
sense when Opportunism is allowed.
|
||||
|
||||
|
||||
For routing and firewalling, Pluto invokes the updown script. Pluto
|
||||
installs eroutes via extended PF_KEY messages.
|
||||
|
||||
|
||||
Current Pluto Behaviour
|
||||
-----------------------
|
||||
|
||||
Data Structures:
|
||||
|
||||
Routes and most eroutes are associated with connections (struct
|
||||
connection, a potential connection description). The enum routing_t
|
||||
field "routing" in struct connection records the state of routing and
|
||||
erouting for that connection. The values are:
|
||||
RT_UNROUTED, /* unrouted */
|
||||
RT_UNROUTED_HOLD, /* unrouted, but HOLD shunt installed */
|
||||
RT_ROUTED_PROSPECTIVE, /* routed, and TRAP shunt installed */
|
||||
RT_ROUTED_HOLD, /* routed, and HOLD shunt installed */
|
||||
RT_ROUTED_FAILURE, /* routed, and failure-context shunt installed */
|
||||
RT_ROUTED_TUNNEL /* routed, and erouted to an IPSEC SA group */
|
||||
Notice that the routing and erouting are not independent: erouting
|
||||
(except for HOLD) implies that the connection is routed.
|
||||
|
||||
Several struct connections may have the same destination subnet. If
|
||||
they agree on what the route should be, they can share it -- any of
|
||||
them may have routing >= RT_ROUTED_PROSPECTIVE. If they disagree,
|
||||
they cannot simultaneously be routed.
|
||||
|
||||
invariant: for all struct connections c, d:
|
||||
(c.that.client == d.that.client
|
||||
&& c.routing >= RT_ROUTED_PROSPECTIVE
|
||||
&& d.routing >= RT_ROUTED_PROSPECTIVE)
|
||||
=> c.interface == d.interface && c.this.nexthop == d.this.nexthop
|
||||
|
||||
There are two kinds of eroutes: shunt eroutes and ones for an IPSEC SA
|
||||
Group. Most eroutes are associated with and are represeented in a
|
||||
connection. The exception is that some HOLD and PASS shunts do not
|
||||
correspond to connections; those are represented in the bare_shunt
|
||||
table.
|
||||
|
||||
An eroute for an IPSEC SA Group is associated with the state object
|
||||
for that Group. The existence of such an eroute is also represented
|
||||
by the "so_serial_t eroute_owner" field in the struct connection. The
|
||||
value is the serial number of the state object for the Group. The
|
||||
special value SOS_NOBODY means that there is no owner associated with
|
||||
this connection for the eroute and hence no normal eroute. At most
|
||||
one eroute owner may exist for a particular (source subnet,
|
||||
destination subnet) pair. A Pluto-managed eroute cannot be associated
|
||||
with an RT_UNROUTED connection.
|
||||
|
||||
invariant: for all struct connection c:
|
||||
c.routing == RT_EROUTED_TUNNEL || c.eroute_owner == SOS_NOBODY
|
||||
|
||||
invariant: for all struct connections c, d:
|
||||
c.this.client == d.this.client && c.that.client == d.that.client
|
||||
&& &c != &d
|
||||
=> c.routing == RT_UNROUTED || d.routing == RT_UNROUTED
|
||||
|
||||
If no normal eroute is set for a particular (source subnet,
|
||||
destination subnet) pair for which a connection is routed, then a
|
||||
shunt eroute would have been installed. This specifies what should
|
||||
happen to packets snared by the route.
|
||||
|
||||
When Pluto is notified by KLIPS of a packet that has been TRAPped,
|
||||
there is no connection with which to associate the HOLD. It is
|
||||
temporarily held in the "bare_shunt table". If Opportunism is
|
||||
attempted but DNS doesn't provide Security Gateway information, Pluto
|
||||
will replace the HOLD with a PASS shunt. Since this PASS isn't
|
||||
associated with a connection, it too will reside in the bare_shunt
|
||||
table. If the HOLD can be associated with a connection, it will be
|
||||
removed from the bare_shunt table and represented in the connection.
|
||||
|
||||
There are two contexts for which shunt eroutes are installed by Pluto
|
||||
for a particular connection. The first context is with the prospect
|
||||
of dealing with packets before any negotiation has been attempted. I
|
||||
call this context "prospective". Currently is a TRAP shunt, used to
|
||||
catch packets for initiate opportunistic negotiation. In the future,
|
||||
it might also be used to implement preordained PASS, DROP, or REJECT
|
||||
rules.
|
||||
|
||||
The second context is after a failed negotiation. I call this context
|
||||
"failure". At this point a different kind of shunt eroute is
|
||||
appropriate. Depending on policy, it could be PASS, DROP, or REJECT,
|
||||
but it is unlikely to be TRAP. The shunt eroute should have a
|
||||
lifetime (this isn't yet implemented). When the lifetime expires, the
|
||||
failure shunt eroute should be replaced by the prospective shunt
|
||||
eroute.
|
||||
|
||||
The kind and duration of a failure shunt eroute should perhaps depend
|
||||
on the nature of the failure, at least as imperfectly detected by
|
||||
Pluto. We haven't looked at this. In particular, the mapping from
|
||||
observations to robust respose isn't obvious.
|
||||
|
||||
The shunt eroute policies should be a function of the potential
|
||||
connection. The failure shunt eroute can be specified for a
|
||||
particular connection with the flags --pass and --drop in a connection
|
||||
definition. There are four combinations, and each has a distinct
|
||||
meaning. The failure shunt eroute is incompletely implemented and
|
||||
cannot be represented in /etc/ipsec.conf.
|
||||
|
||||
There is as yet no control over the prospective shunt eroute: it is
|
||||
always TRAP as far as Pluto is concerned. This is probably
|
||||
reasonable: any other fate suggests that no negotiation will be done,
|
||||
and so a connection definition is inappropriate. These should be
|
||||
implemented as manual conns. There remains the issue of whether Pluto
|
||||
should be aware of them -- currently it is not.
|
||||
|
||||
|
||||
Routines:
|
||||
|
||||
[in kernel.c]
|
||||
|
||||
bool do_command(struct connection *c, const char *verb)
|
||||
Run the updown script to perform such tasks as installing a route
|
||||
and adjust the firewall.
|
||||
|
||||
bool could_route(struct connection *c)
|
||||
Check to see whether we could route and eroute the connection.
|
||||
<- shunt_eroute_connection (to check if --route can be performed)
|
||||
<- install_inbound_ipsec_sa (to see if it will be possible
|
||||
to (later) install route and eroute the corresponding outbound SA)
|
||||
<- install_ipsec_sa (to see if the outbound SA can be routed and erouted)
|
||||
|
||||
bool trap_connection(struct connection *c)
|
||||
Install a TRAP shunt eroute for this connection. This implements
|
||||
"whack --route", the way an admin can specify that packets for a
|
||||
connection should be caught without first bringing it up.
|
||||
|
||||
void unroute_connection(struct connection *c)
|
||||
Delete any eroute for a connection and unroute it if route isn't shared.
|
||||
<- release_connection
|
||||
<- whack_handle (for "whack --unroute)
|
||||
|
||||
bool eroute_connection(struct connection *c
|
||||
, ipsec_spi_t spi, unsigned int proto, unsigned int satype
|
||||
, unsigned int op, const char *opname UNUSED)
|
||||
Issue PF_KEY commands to KLIPS to add, replace, or delete an eroute.
|
||||
The verb is specified by op and described (for logging) by opname.
|
||||
<- assign_hold
|
||||
<- sag_eroute
|
||||
<- shunt_eroute
|
||||
|
||||
bool assign_hold(struct connection *c
|
||||
, const ip_address *src, const ip_address *dst)
|
||||
Take a HOLD from the bare_shunt table and assign it to a connection.
|
||||
If the HOLD is broadened (i.e. the connection's source or destination
|
||||
subnets contain more than one IP address), this will involve replacing
|
||||
the HOLD with a different one.
|
||||
|
||||
bool sag_eroute(struct state *st, unsigned op, const char *opname)
|
||||
SA Group eroute manipulation. The SA Group concerned is
|
||||
identified with a state object.
|
||||
<- route_and_eroute several times
|
||||
|
||||
bool shunt_eroute(struct connection *c, unsigned int op, const char *opname)
|
||||
shunt eroute manipulation. Shunt eroutes are associated with
|
||||
connections.
|
||||
<- unroute_connection
|
||||
<- route_and_eroute
|
||||
<- delete_ipsec_sa
|
||||
|
||||
bool route_and_eroute(struct connection *c, struct state *st)
|
||||
Install a route and then a prospective shunt eroute or an SA group
|
||||
eroute. The code assumes that could_route had previously
|
||||
given the go-ahead. Any SA group to be erouted must already
|
||||
exist.
|
||||
<- shunt_eroute_connection
|
||||
<- install_ipsec_sa
|
||||
|
||||
void scan_proc_shunts(void)
|
||||
Every SHUNT_SCAN_INTERVAL scan /proc/net/ipsec_eroute.
|
||||
Delete any PASS eroute in the bare_shunt table that hasn't been used
|
||||
within the last SHUNT_PATIENCE seconds.
|
||||
For any HOLD for which Pluto hasn't received an ACQUIRE (possibly
|
||||
lost due to congestion), act as if an ACQUIRE were received.
|
||||
|
||||
[in connection.c]
|
||||
|
||||
struct connection *route_owner(struct connection *c, struct connection **erop)
|
||||
Find the connection to connection c's peer's client with the
|
||||
largest value of .routing. All other things being equal,
|
||||
preference is given to c. Return NULL if no connection is routed
|
||||
at all. If erop is non-null, sets it to a connection sharing both
|
||||
our client subnet and peer's client subnet with the largest value
|
||||
of .routing.
|
||||
The return value is used to find other connections sharing
|
||||
a route. The value of *erop is used to find other connections
|
||||
sharing an eroute.
|
||||
<- could_route (to find any conflicting routes or eroutes)
|
||||
<- unroute_connection (to find out if our route is still in use
|
||||
after this connection is finished with it)
|
||||
<- install_inbound_ipsec_sa (to find other IPSEC SAs for the
|
||||
same peer clients; when we find them WE KILL THEM; a
|
||||
kludge to deal with road warriors reconnecting)
|
||||
<- route_and_eroute (to find all the connections from which the
|
||||
route or eroute is being stolen)
|
||||
|
||||
Uses:
|
||||
|
||||
- setting up route & shunt eroute to TRAP packets for opportunism
|
||||
(whack --route). Perhaps also manually designating DROP, REJECT, or
|
||||
PASS for certain packets.
|
||||
|
||||
whack_handle() responds to --route; calls route_connection()
|
||||
|
||||
|
||||
- removing same (whack --unroute)
|
||||
|
||||
whack_handle() responds to --unroute; calls unroute_connection()
|
||||
|
||||
- installing route & normal eroute for a newly negotiated group of
|
||||
outbound IPSEC SAs
|
||||
|
||||
+ perhaps an (additional) route is not needed: if the negotiation
|
||||
was initiated by a TRAPped outgoing packet, then there must
|
||||
already have been a route that got the packet to ipsecN. Mind
|
||||
you, it could have been the wrong N!
|
||||
|
||||
install_ipsec_sa()
|
||||
|
||||
- updating a normal eroute when a new group of IPSEC SAs replaces
|
||||
an old one due to rekeying.
|
||||
|
||||
install_ipsec_sa()
|
||||
|
||||
- replacing an old eroute when a negotiation fails. But this is
|
||||
tricky. If this was a rekeying, we should just leave the old
|
||||
normal eroute be -- it might still work. Otherwise, this was
|
||||
an initial negotiation: we should replace the shunt eroute
|
||||
with one appropriate for the failure context.
|
||||
|
||||
- when a group of IPSEC SAs dies or is killed, and it had the eroute,
|
||||
its normal eroute should be replaced by a shunt eroute. If there
|
||||
was an attempt to replace the group, the replacement is in the
|
||||
failure context; otherwise the replacement is in the prospective
|
||||
context.
|
||||
Reference in New Issue
Block a user