Coverage Report

Created: 2026-08-13 07:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/strongswan/src/libcharon/sa/child_sa_manager.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2014 Martin Willi
3
 *
4
 * Copyright (C) secunet Security Networks AG
5
 *
6
 * This program is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License as published by the
8
 * Free Software Foundation; either version 2 of the License, or (at your
9
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
10
 *
11
 * This program is distributed in the hope that it will be useful, but
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14
 * for more details.
15
 */
16
17
#include "child_sa_manager.h"
18
19
#include <daemon.h>
20
#include <threading/mutex.h>
21
#include <collections/hashtable.h>
22
23
typedef struct private_child_sa_manager_t private_child_sa_manager_t;
24
25
/**
26
 * Private data of an child_sa_manager_t object.
27
 */
28
struct private_child_sa_manager_t {
29
30
  /**
31
   * Public child_sa_manager_t interface.
32
   */
33
  child_sa_manager_t public;
34
35
  /**
36
   * CHILD_SAs by inbound SPI/dst, child_entry_t => child_entry_t
37
   */
38
  hashtable_t *in;
39
40
  /**
41
   * CHILD_SAs by outbound SPI/dst, child_entry_t => child_entry_t
42
   */
43
  hashtable_t *out;
44
45
  /**
46
   * CHILD_SAs by unique ID, child_entry_t => child_entry_t
47
   */
48
  hashtable_t *ids;
49
50
  /**
51
   * Mutex to access any hashtable
52
   */
53
  mutex_t *mutex;
54
};
55
56
/**
57
 * Hashtable entry for a known CHILD_SA
58
 */
59
typedef struct {
60
  /** the associated IKE_SA */
61
  ike_sa_id_t *ike_id;
62
  /** unique CHILD_SA identifier */
63
  uint32_t unique_id;
64
  /** inbound SPI */
65
  uint32_t spi_in;
66
  /** outbound SPI */
67
  uint32_t spi_out;
68
  /** inbound host address */
69
  host_t *host_in;
70
  /** outbound host address and port */
71
  host_t *host_out;
72
  /** IPsec protocol, AH|ESP */
73
  protocol_id_t proto;
74
} child_entry_t;
75
76
/**
77
 * Destroy a CHILD_SA entry
78
 */
79
static void child_entry_destroy(child_entry_t *entry)
80
0
{
81
0
  entry->ike_id->destroy(entry->ike_id);
82
0
  entry->host_in->destroy(entry->host_in);
83
0
  entry->host_out->destroy(entry->host_out);
84
0
  free(entry);
85
0
}
86
87
/**
88
 * Hashtable hash function for inbound SAs
89
 */
90
static u_int hash_in(child_entry_t *entry)
91
0
{
92
0
  return chunk_hash_inc(chunk_from_thing(entry->spi_in),
93
0
      chunk_hash_inc(entry->host_in->get_address(entry->host_in),
94
0
       chunk_hash(chunk_from_thing(entry->proto))));
95
0
}
96
97
/**
98
 * Hashtable equals function for inbound SAs
99
 */
100
static bool equals_in(child_entry_t *a, child_entry_t *b)
101
0
{
102
0
  return a->spi_in == b->spi_in &&
103
0
       a->proto == b->proto &&
104
0
       a->host_in->ip_equals(a->host_in, b->host_in);
105
0
}
106
107
/**
108
 * Hashtable hash function for outbound SAs
109
 */
110
static u_int hash_out(child_entry_t *entry)
111
0
{
112
0
  return chunk_hash_inc(chunk_from_thing(entry->spi_out),
113
0
      chunk_hash_inc(entry->host_out->get_address(entry->host_out),
114
0
       chunk_hash(chunk_from_thing(entry->proto))));
115
0
}
116
117
/**
118
 * Hashtable equals function for outbound SAs
119
 */
120
static bool equals_out(child_entry_t *a, child_entry_t *b)
121
0
{
122
0
  return a->spi_out == b->spi_out &&
123
0
       a->proto == b->proto &&
124
0
       a->host_out->ip_equals(a->host_out, b->host_out);
125
0
}
126
127
/**
128
 * Hashtable hash function for SAs by unique ID
129
 */
130
static u_int hash_id(child_entry_t *entry)
131
0
{
132
0
  return chunk_hash(chunk_from_thing(entry->unique_id));
133
0
}
134
135
/**
136
 * Hashtable equals function for SAs by unique ID
137
 */
138
static bool equals_id(child_entry_t *a, child_entry_t *b)
139
0
{
140
0
  return a->unique_id == b->unique_id;
141
0
}
142
143
METHOD(child_sa_manager_t, add, void,
144
  private_child_sa_manager_t *this, child_sa_t *child_sa, ike_sa_t *ike_sa)
145
0
{
146
0
  child_entry_t *entry, *replaced;
147
0
  host_t *in, *out;
148
0
  ike_sa_id_t *id;
149
150
0
  id = ike_sa->get_id(ike_sa);
151
0
  in = ike_sa->get_my_host(ike_sa);
152
0
  out = ike_sa->get_other_host(ike_sa);
153
154
0
  INIT(entry,
155
0
    .ike_id = id->clone(id),
156
0
    .unique_id = child_sa->get_unique_id(child_sa),
157
0
    .proto = child_sa->get_protocol(child_sa),
158
0
    .spi_in = child_sa->get_spi(child_sa, TRUE),
159
0
    .spi_out = child_sa->get_spi(child_sa, FALSE),
160
0
    .host_in = in->clone(in),
161
0
    .host_out = out->clone(out),
162
0
  );
163
164
0
  this->mutex->lock(this->mutex);
165
0
  if (!this->in->get(this->in, entry) &&
166
0
    !this->out->get(this->out, entry))
167
0
  {
168
0
    replaced = this->ids->put(this->ids, entry, entry);
169
0
    if (replaced)
170
0
    { /* remove the replaced entry in the other tables in case the unique
171
       * ID got reused */
172
0
      this->in->remove(this->in, replaced);
173
0
      this->out->remove(this->out, replaced);
174
0
    }
175
0
    this->in->put(this->in, entry, entry);
176
0
    this->out->put(this->out, entry, entry);
177
0
    entry = replaced;
178
0
  }
179
0
  this->mutex->unlock(this->mutex);
180
181
0
  if (entry)
182
0
  {
183
0
    child_entry_destroy(entry);
184
0
  }
185
0
}
186
187
METHOD(child_sa_manager_t, remove_, void,
188
  private_child_sa_manager_t *this, child_sa_t *child_sa)
189
0
{
190
0
  child_entry_t *entry, key = {
191
0
    .unique_id = child_sa->get_unique_id(child_sa),
192
0
  };
193
194
0
  this->mutex->lock(this->mutex);
195
0
  entry = this->ids->remove(this->ids, &key);
196
0
  if (entry)
197
0
  {
198
0
    this->in->remove(this->in, entry);
199
0
    this->out->remove(this->out, entry);
200
0
  }
201
0
  this->mutex->unlock(this->mutex);
202
203
0
  if (entry)
204
0
  {
205
0
    child_entry_destroy(entry);
206
0
  }
207
0
}
208
209
/**
210
 * Check out an IKE_SA for a given CHILD_SA
211
 */
212
static ike_sa_t *checkout_ikesa(private_child_sa_manager_t *this,
213
          ike_sa_id_t *id, uint32_t unique_id, child_sa_t **child_sa)
214
0
{
215
0
  enumerator_t *enumerator;
216
0
  child_sa_t *current;
217
0
  ike_sa_t *ike_sa;
218
0
  bool found = FALSE;
219
220
0
  ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
221
0
  id->destroy(id);
222
0
  if (ike_sa)
223
0
  {
224
0
    enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
225
0
    while (enumerator->enumerate(enumerator, &current))
226
0
    {
227
0
      found = current->get_unique_id(current) == unique_id;
228
0
      if (found)
229
0
      {
230
0
        if (child_sa)
231
0
        {
232
0
          *child_sa = current;
233
0
        }
234
0
        break;
235
0
      }
236
0
    }
237
0
    enumerator->destroy(enumerator);
238
239
0
    if (found)
240
0
    {
241
0
      return ike_sa;
242
0
    }
243
0
    charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
244
0
  }
245
0
  return NULL;
246
0
}
247
248
METHOD(child_sa_manager_t, checkout_by_id, ike_sa_t*,
249
  private_child_sa_manager_t *this, uint32_t unique_id,
250
  child_sa_t **child_sa)
251
0
{
252
0
  ike_sa_id_t *id;
253
0
  child_entry_t *entry, key = {
254
0
    .unique_id = unique_id,
255
0
  };
256
257
0
  this->mutex->lock(this->mutex);
258
0
  entry = this->ids->get(this->ids, &key);
259
0
  if (entry)
260
0
  {
261
0
    id = entry->ike_id->clone(entry->ike_id);
262
0
  }
263
0
  this->mutex->unlock(this->mutex);
264
265
0
  if (entry)
266
0
  {
267
0
    return checkout_ikesa(this, id, unique_id, child_sa);
268
0
  }
269
0
  return NULL;
270
0
}
271
272
METHOD(child_sa_manager_t, checkout, ike_sa_t*,
273
  private_child_sa_manager_t *this, protocol_id_t protocol, uint32_t spi,
274
  host_t *dst, child_sa_t **child_sa)
275
0
{
276
0
  ike_sa_id_t *id;
277
0
  uint32_t unique_id;
278
0
  child_entry_t *entry, key = {
279
0
    .spi_in = spi,
280
0
    .spi_out = spi,
281
0
    .host_in = dst,
282
0
    .host_out = dst,
283
0
    .proto = protocol,
284
0
  };
285
286
0
  this->mutex->lock(this->mutex);
287
0
  entry = this->in->get(this->in, &key);
288
0
  if (!entry)
289
0
  {
290
0
    entry = this->out->get(this->out, &key);
291
0
  }
292
0
  if (entry)
293
0
  {
294
0
    unique_id = entry->unique_id;
295
0
    id = entry->ike_id->clone(entry->ike_id);
296
0
  }
297
0
  this->mutex->unlock(this->mutex);
298
299
0
  if (entry)
300
0
  {
301
0
    return checkout_ikesa(this, id, unique_id, child_sa);
302
0
  }
303
0
  return NULL;
304
0
}
305
306
METHOD(child_sa_manager_t, destroy, void,
307
  private_child_sa_manager_t *this)
308
0
{
309
0
  this->in->destroy(this->in);
310
0
  this->out->destroy(this->out);
311
0
  this->ids->destroy(this->ids);
312
0
  this->mutex->destroy(this->mutex);
313
0
  free(this);
314
0
}
315
316
/**
317
 * See header
318
 */
319
child_sa_manager_t *child_sa_manager_create()
320
0
{
321
0
  private_child_sa_manager_t *this;
322
323
0
  INIT(this,
324
0
    .public = {
325
0
      .add = _add,
326
0
      .remove = _remove_,
327
0
      .checkout = _checkout,
328
0
      .checkout_by_id = _checkout_by_id,
329
0
      .destroy = _destroy,
330
0
    },
331
0
    .in = hashtable_create((hashtable_hash_t)hash_in,
332
0
                 (hashtable_equals_t)equals_in, 8),
333
0
    .out = hashtable_create((hashtable_hash_t)hash_out,
334
0
                 (hashtable_equals_t)equals_out, 8),
335
0
    .ids = hashtable_create((hashtable_hash_t)hash_id,
336
0
                 (hashtable_equals_t)equals_id, 8),
337
0
    .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
338
0
  );
339
340
0
  return &this->public;
341
0
}