Send NAS-Port, NAS-IP and Calling/Called-Station-ID in Accounting-Requests
[strongswan.git] / src / libcharon / plugins / eap_radius / eap_radius_accounting.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "eap_radius_accounting.h"
17 #include "eap_radius_plugin.h"
18
19 #include <time.h>
20
21 #include <radius_message.h>
22 #include <radius_client.h>
23 #include <daemon.h>
24 #include <collections/hashtable.h>
25 #include <threading/mutex.h>
26
27 typedef struct private_eap_radius_accounting_t private_eap_radius_accounting_t;
28
29 /**
30 * Private data of an eap_radius_accounting_t object.
31 */
32 struct private_eap_radius_accounting_t {
33
34 /**
35 * Public eap_radius_accounting_t interface.
36 */
37 eap_radius_accounting_t public;
38
39 /**
40 * Hashtable with sessions, IKE_SA unique id => entry_t
41 */
42 hashtable_t *sessions;
43
44 /**
45 * Mutex to lock sessions
46 */
47 mutex_t *mutex;
48
49 /**
50 * Session ID prefix
51 */
52 u_int32_t prefix;
53 };
54
55 /**
56 * Hashtable entry with usage stats
57 */
58 typedef struct {
59 /** RADIUS accounting session ID */
60 char sid[16];
61 /** number of sent/received octets/packets */
62 struct {
63 u_int64_t sent;
64 u_int64_t received;
65 } bytes, packets;
66 /** session creation time */
67 time_t created;
68 } entry_t;
69
70 /**
71 * Accounting message status types
72 */
73 typedef enum {
74 ACCT_STATUS_START = 1,
75 ACCT_STATUS_STOP = 2,
76 ACCT_STATUS_INTERIM_UPDATE = 3,
77 ACCT_STATUS_ACCOUNTING_ON = 7,
78 ACCT_STATUS_ACCOUNTING_OFF = 8,
79 } radius_acct_status_t;
80
81 /**
82 * Hashtable hash function
83 */
84 static u_int hash(uintptr_t key)
85 {
86 return key;
87 }
88
89 /**
90 * Hashtable equals function
91 */
92 static bool equals(uintptr_t a, uintptr_t b)
93 {
94 return a == b;
95 }
96
97 /**
98 * Update usage counter when a CHILD_SA rekeys/goes down
99 */
100 static void update_usage(private_eap_radius_accounting_t *this,
101 ike_sa_t *ike_sa, child_sa_t *child_sa)
102 {
103 u_int64_t bytes_in, bytes_out, packets_in, packets_out;
104 entry_t *entry;
105
106 child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, &packets_out);
107 child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in, &packets_in);
108
109 this->mutex->lock(this->mutex);
110 entry = this->sessions->get(this->sessions,
111 (void*)(uintptr_t)ike_sa->get_unique_id(ike_sa));
112 if (entry)
113 {
114 entry->bytes.sent += bytes_out;
115 entry->bytes.received += bytes_in;
116 entry->packets.sent += packets_out;
117 entry->packets.received += packets_in;
118 }
119 this->mutex->unlock(this->mutex);
120 }
121
122 /**
123 * Send a RADIUS message, wait for response
124 */
125 static bool send_message(private_eap_radius_accounting_t *this,
126 radius_message_t *request)
127 {
128 radius_message_t *response;
129 radius_client_t *client;
130 bool ack = FALSE;
131
132 client = eap_radius_create_client();
133 if (client)
134 {
135 response = client->request(client, request);
136 if (response)
137 {
138 ack = response->get_code(response) == RMC_ACCOUNTING_RESPONSE;
139 response->destroy(response);
140 }
141 else
142 {
143 charon->bus->alert(charon->bus, ALERT_RADIUS_NOT_RESPONDING);
144 }
145 client->destroy(client);
146 }
147 return ack;
148 }
149
150 /**
151 * Add common IKE_SA parameters to RADIUS account message
152 */
153 static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa)
154 {
155 enumerator_t *enumerator;
156 host_t *vip, *host;
157 char buf[64];
158 chunk_t data;
159 u_int32_t value;
160
161 /* virtual NAS-Port-Type */
162 value = htonl(5);
163 message->add(message, RAT_NAS_PORT_TYPE, chunk_from_thing(value));
164 /* framed ServiceType */
165 value = htonl(2);
166 message->add(message, RAT_SERVICE_TYPE, chunk_from_thing(value));
167
168 value = htonl(ike_sa->get_unique_id(ike_sa));
169 message->add(message, RAT_NAS_PORT, chunk_from_thing(value));
170 message->add(message, RAT_NAS_PORT_ID,
171 chunk_from_str(ike_sa->get_name(ike_sa)));
172
173 host = ike_sa->get_my_host(ike_sa);
174 data = host->get_address(host);
175 switch (host->get_family(host))
176 {
177 case AF_INET:
178 message->add(message, RAT_NAS_IP_ADDRESS, data);
179 break;
180 case AF_INET6:
181 message->add(message, RAT_NAS_IPV6_ADDRESS, data);
182 default:
183 break;
184 }
185 snprintf(buf, sizeof(buf), "%#H", host);
186 message->add(message, RAT_CALLED_STATION_ID, chunk_from_str(buf));
187 host = ike_sa->get_other_host(ike_sa);
188 snprintf(buf, sizeof(buf), "%#H", host);
189 message->add(message, RAT_CALLING_STATION_ID, chunk_from_str(buf));
190
191 snprintf(buf, sizeof(buf), "%Y", ike_sa->get_other_eap_id(ike_sa));
192 message->add(message, RAT_USER_NAME, chunk_from_str(buf));
193
194 enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE);
195 while (enumerator->enumerate(enumerator, &vip))
196 {
197 switch (vip->get_family(vip))
198 {
199 case AF_INET:
200 message->add(message, RAT_FRAMED_IP_ADDRESS,
201 vip->get_address(vip));
202 break;
203 case AF_INET6:
204 /* we currently assign /128 prefixes, only (reserved, length) */
205 data = chunk_from_chars(0, 128);
206 data = chunk_cata("cc", data, vip->get_address(vip));
207 message->add(message, RAT_FRAMED_IPV6_PREFIX, data);
208 break;
209 default:
210 break;
211 }
212 }
213 enumerator->destroy(enumerator);
214 }
215
216 /**
217 * Send an accounting start message
218 */
219 static void send_start(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
220 {
221 radius_message_t *message;
222 entry_t *entry;
223 u_int32_t id, value;
224
225 id = ike_sa->get_unique_id(ike_sa);
226 INIT(entry,
227 .created = time_monotonic(NULL),
228 );
229 snprintf(entry->sid, sizeof(entry->sid), "%u-%u", this->prefix, id);
230
231 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
232 value = htonl(ACCT_STATUS_START);
233 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
234 message->add(message, RAT_ACCT_SESSION_ID,
235 chunk_create(entry->sid, strlen(entry->sid)));
236 add_ike_sa_parameters(message, ike_sa);
237 if (send_message(this, message))
238 {
239 this->mutex->lock(this->mutex);
240 entry = this->sessions->put(this->sessions, (void*)(uintptr_t)id, entry);
241 this->mutex->unlock(this->mutex);
242 }
243 message->destroy(message);
244 free(entry);
245 }
246
247 /**
248 * Send an account stop message
249 */
250 static void send_stop(private_eap_radius_accounting_t *this, ike_sa_t *ike_sa)
251 {
252 radius_message_t *message;
253 entry_t *entry;
254 u_int32_t id, value;
255
256 id = ike_sa->get_unique_id(ike_sa);
257 this->mutex->lock(this->mutex);
258 entry = this->sessions->remove(this->sessions, (void*)(uintptr_t)id);
259 this->mutex->unlock(this->mutex);
260 if (entry)
261 {
262 message = radius_message_create(RMC_ACCOUNTING_REQUEST);
263 value = htonl(ACCT_STATUS_STOP);
264 message->add(message, RAT_ACCT_STATUS_TYPE, chunk_from_thing(value));
265 message->add(message, RAT_ACCT_SESSION_ID,
266 chunk_create(entry->sid, strlen(entry->sid)));
267 add_ike_sa_parameters(message, ike_sa);
268
269 value = htonl(entry->bytes.sent);
270 message->add(message, RAT_ACCT_OUTPUT_OCTETS, chunk_from_thing(value));
271 value = htonl(entry->bytes.sent >> 32);
272 if (value)
273 {
274 message->add(message, RAT_ACCT_OUTPUT_GIGAWORDS,
275 chunk_from_thing(value));
276 }
277 value = htonl(entry->packets.sent);
278 message->add(message, RAT_ACCT_OUTPUT_PACKETS, chunk_from_thing(value));
279
280 value = htonl(entry->bytes.received);
281 message->add(message, RAT_ACCT_INPUT_OCTETS, chunk_from_thing(value));
282 value = htonl(entry->bytes.received >> 32);
283 if (value)
284 {
285 message->add(message, RAT_ACCT_INPUT_GIGAWORDS,
286 chunk_from_thing(value));
287 }
288 value = htonl(entry->packets.received);
289 message->add(message, RAT_ACCT_INPUT_PACKETS, chunk_from_thing(value));
290
291 value = htonl(time_monotonic(NULL) - entry->created);
292 message->add(message, RAT_ACCT_SESSION_TIME, chunk_from_thing(value));
293
294 send_message(this, message);
295 message->destroy(message);
296 free(entry);
297 }
298 }
299
300 METHOD(listener_t, ike_updown, bool,
301 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa, bool up)
302 {
303 if (!up)
304 {
305 enumerator_t *enumerator;
306 child_sa_t *child_sa;
307
308 /* update usage for all children just before sending stop */
309 enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
310 while (enumerator->enumerate(enumerator, &child_sa))
311 {
312 update_usage(this, ike_sa, child_sa);
313 }
314 enumerator->destroy(enumerator);
315
316 send_stop(this, ike_sa);
317 }
318 return TRUE;
319 }
320
321 METHOD(listener_t, message_hook, bool,
322 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
323 message_t *message, bool incoming, bool plain)
324 {
325 /* start accounting here, virtual IP now is set */
326 if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
327 !incoming && !message->get_request(message))
328 {
329 if (ike_sa->get_version(ike_sa) == IKEV1 &&
330 message->get_exchange_type(message) == TRANSACTION)
331 {
332 send_start(this, ike_sa);
333 }
334 if (ike_sa->get_version(ike_sa) == IKEV2 &&
335 message->get_exchange_type(message) == IKE_AUTH)
336 {
337 send_start(this, ike_sa);
338 }
339 }
340 return TRUE;
341 }
342
343 METHOD(listener_t, ike_rekey, bool,
344 private_eap_radius_accounting_t *this, ike_sa_t *old, ike_sa_t *new)
345 {
346 entry_t *entry;
347
348 this->mutex->lock(this->mutex);
349 entry = this->sessions->remove(this->sessions,
350 (void*)(uintptr_t)old->get_unique_id(old));
351 if (entry)
352 {
353 entry = this->sessions->put(this->sessions,
354 (void*)(uintptr_t)new->get_unique_id(new), entry);
355 if (entry)
356 {
357 free(entry);
358 }
359 }
360 this->mutex->unlock(this->mutex);
361
362 return TRUE;
363 }
364
365 METHOD(listener_t, child_rekey, bool,
366 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
367 child_sa_t *old, child_sa_t *new)
368 {
369 update_usage(this, ike_sa, old);
370
371 return TRUE;
372 }
373
374 METHOD(listener_t, child_updown, bool,
375 private_eap_radius_accounting_t *this, ike_sa_t *ike_sa,
376 child_sa_t *child_sa, bool up)
377 {
378 if (!up && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
379 {
380 update_usage(this, ike_sa, child_sa);
381 }
382 return TRUE;
383 }
384
385 METHOD(eap_radius_accounting_t, destroy, void,
386 private_eap_radius_accounting_t *this)
387 {
388 this->mutex->destroy(this->mutex);
389 this->sessions->destroy(this->sessions);
390 free(this);
391 }
392
393 /**
394 * See header
395 */
396 eap_radius_accounting_t *eap_radius_accounting_create()
397 {
398 private_eap_radius_accounting_t *this;
399
400 INIT(this,
401 .public = {
402 .listener = {
403 .ike_updown = _ike_updown,
404 .ike_rekey = _ike_rekey,
405 .message = _message_hook,
406 .child_updown = _child_updown,
407 .child_rekey = _child_rekey,
408 },
409 .destroy = _destroy,
410 },
411 /* use system time as Session ID prefix */
412 .prefix = (u_int32_t)time(NULL),
413 .sessions = hashtable_create((hashtable_hash_t)hash,
414 (hashtable_equals_t)equals, 32),
415 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
416 );
417
418 return &this->public;
419 }