1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
|
Routing and Erouting in Pluto
=============================
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.
|