acct.c 13.1 KB
Newer Older
1
#include <stdlib.h>
2
#include <stdio.h>
3 4 5 6
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
K
Kozlov Dmitry 已提交
7
#include <netinet/in.h>
K
Kozlov Dmitry 已提交
8 9
#include "linux_ppp.h"

10
#include "crypto.h"
11 12

#include "log.h"
13
#include "radius_p.h"
14

D
Dmitry Kozlov 已提交
15 16
#include "memdebug.h"

17
#define STAT_UPDATE_INTERVAL (10 * 60 * 1000)
18
#define INTERIM_SAFE_TIME 10
19

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
static int req_set_RA(struct rad_req_t *req, const char *secret)
{
	MD5_CTX ctx;
	
	if (rad_packet_build(req->pack, req->RA))
		return -1;

	MD5_Init(&ctx);
	MD5_Update(&ctx, req->pack->buf, req->pack->len);
	MD5_Update(&ctx, secret, strlen(secret));
	MD5_Final(req->pack->buf + 4, &ctx);

	return 0;
}

static void req_set_stat(struct rad_req_t *req, struct ppp_t *ppp)
{
37
	struct rtnl_link_stats stats;
38 39 40 41 42 43
	time_t stop_time;
	
	if (ppp->stop_time)
		stop_time = ppp->stop_time;
	else
		time(&stop_time);
44

45 46 47 48 49 50 51
	if (ppp_read_stats(ppp, &stats) == 0) {
		rad_packet_change_int(req->pack, NULL, "Acct-Input-Octets", stats.rx_bytes);
		rad_packet_change_int(req->pack, NULL, "Acct-Output-Octets", stats.tx_bytes);
		rad_packet_change_int(req->pack, NULL, "Acct-Input-Packets", stats.rx_packets);
		rad_packet_change_int(req->pack, NULL, "Acct-Output-Packets", stats.tx_packets);
		rad_packet_change_int(req->pack, NULL, "Acct-Input-Gigawords", ppp->acct_input_gigawords);
		rad_packet_change_int(req->pack, NULL, "Acct-Output-Gigawords", ppp->acct_output_gigawords);
52
	}
K
Kozlov Dmitry 已提交
53
	rad_packet_change_int(req->pack, NULL, "Acct-Session-Time", stop_time - ppp->start_time);
54 55 56 57 58
}

static int rad_acct_read(struct triton_md_handler_t *h)
{
	struct rad_req_t *req = container_of(h, typeof(*req), hnd);
K
Kozlov Dmitry 已提交
59 60
	struct rad_packet_t *pack;
	int r;
61
	unsigned int dt;
62

K
Kozlov Dmitry 已提交
63
	if (req->reply) {
D
Dmitry Kozlov 已提交
64
		rad_packet_free(req->reply);
K
Kozlov Dmitry 已提交
65 66 67 68 69 70 71
		req->reply = NULL;
	}

	while (1) {
		r = rad_packet_recv(h->fd, &pack, NULL);

		if (pack) {
72
			rad_server_reply(req->serv);
K
Kozlov Dmitry 已提交
73 74 75
			if (req->reply)
				rad_packet_free(req->reply);
			req->reply = pack;
76
			if (conf_interim_verbose) {
77
				log_ppp_info2("recv ");
78
				rad_packet_print(req->reply, req->serv, log_ppp_info2);
K
Kozlov Dmitry 已提交
79 80 81 82 83 84
			}
		}

		if (r)
			break;
	}
D
Dmitry Kozlov 已提交
85

86 87 88
	if (!req->reply)
		return 0;

89
	dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 + 
90
		(req->reply->tv.tv_nsec - req->pack->tv.tv_nsec) / 1000000;
K
Kozlov Dmitry 已提交
91 92 93

	stat_accm_add(req->serv->stat_interim_query_1m, dt);
	stat_accm_add(req->serv->stat_interim_query_5m, dt);
94

95 96 97 98
	if (req->reply->code != CODE_ACCOUNTING_RESPONSE || req->reply->id != req->pack->id) {
		rad_packet_free(req->reply);
		req->reply = NULL;
	} else {
D
Dmitry Kozlov 已提交
99 100
		if (req->timeout.tpd)
			triton_timer_del(&req->timeout);
101 102 103 104 105
	}

	return 0;
}

K
temp  
Kozlov Dmitry 已提交
106 107 108 109
static void __rad_req_send(struct rad_req_t *req)
{
	while (1) {
		if (rad_server_req_enter(req)) {
110
			if (rad_server_realloc(req)) {
K
temp  
Kozlov Dmitry 已提交
111
				if (conf_acct_timeout) {
112
					log_ppp_warn("radius:acct: no servers available, terminating session...\n");
K
temp  
Kozlov Dmitry 已提交
113 114 115 116 117 118 119 120
					ppp_terminate(req->rpd->ppp, TERM_NAS_ERROR, 0);
				}
				break;
			}
			continue;
		}

		rad_req_send(req, conf_interim_verbose);
121 122 123 124
		if (!req->hnd.tpd) {
			triton_md_register_handler(req->rpd->ppp->ctrl->ctx, &req->hnd);
			triton_md_enable_handler(&req->hnd, MD_MODE_READ);
		}
K
temp  
Kozlov Dmitry 已提交
125 126 127 128 129 130 131

		rad_server_req_exit(req);

		break;
	}
}

132 133 134
static void rad_acct_timeout(struct triton_timer_t *t)
{
	struct rad_req_t *req = container_of(t, typeof(*req), timeout);
135 136
	time_t ts, dt;

K
Kozlov Dmitry 已提交
137 138 139
	__sync_add_and_fetch(&req->serv->stat_interim_lost, 1);
	stat_accm_add(req->serv->stat_interim_lost_1m, 1);
	stat_accm_add(req->serv->stat_interim_lost_5m, 1);
140 141 142 143 144 145 146

	if (conf_acct_timeout == 0) {
		rad_server_timeout(req->serv);
		triton_timer_del(t);
		return;
	}

147 148 149 150 151
	time(&ts);

	dt = ts - req->rpd->acct_timestamp;

	if (dt > conf_acct_timeout) {
K
temp  
Kozlov Dmitry 已提交
152
		rad_server_fail(req->serv);
153
		if (rad_server_realloc(req)) {
K
temp  
Kozlov Dmitry 已提交
154 155 156 157 158
			log_ppp_warn("radius:acct: no servers available, terminating session...\n");
			ppp_terminate(req->rpd->ppp, TERM_NAS_ERROR, 0);
			return;
		}
		time(&req->rpd->acct_timestamp);
159 160 161 162 163 164 165 166 167 168
	}
	if (dt > conf_acct_timeout / 2) {
		req->timeout.period += 1000;
		triton_timer_mod(&req->timeout, 0);
	} else if (dt > conf_acct_timeout / 3) {
		if (req->timeout.period != conf_timeout * 2000) {
			req->timeout.period = conf_timeout * 2000;
			triton_timer_mod(&req->timeout, 0);
		}
	}
169

170 171 172
	if (conf_acct_delay_time) {
		req->pack->id++;	
		rad_packet_change_int(req->pack, NULL, "Acct-Delay-Time", dt);
173
		req_set_RA(req, req->serv->secret);
174 175
	}

K
temp  
Kozlov Dmitry 已提交
176 177
	__rad_req_send(req);

K
Kozlov Dmitry 已提交
178
	__sync_add_and_fetch(&req->serv->stat_interim_sent, 1);
179 180 181 182 183 184
}

static void rad_acct_interim_update(struct triton_timer_t *t)
{
	struct radius_pd_t *rpd = container_of(t, typeof(*rpd), acct_interim_timer);

185
	if (rpd->acct_req->timeout.tpd)
186 187
		return;

188 189 190 191
	if (rpd->session_timeout.expire_tv.tv_sec && 
			rpd->session_timeout.expire_tv.tv_sec - (time(NULL) - rpd->ppp->start_time) < INTERIM_SAFE_TIME)
			return;

192
	req_set_stat(rpd->acct_req, rpd->ppp);
193 194 195
	if (!rpd->acct_interim_interval)
		return;

196
	time(&rpd->acct_timestamp);
197 198
	rpd->acct_req->pack->id++;

K
Kozlov Dmitry 已提交
199
	rad_packet_change_val(rpd->acct_req->pack, NULL, "Acct-Status-Type", "Interim-Update");
200 201
	if (conf_acct_delay_time)
		rad_packet_change_int(rpd->acct_req->pack, NULL, "Acct-Delay-Time", 0);
202
	req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret);
K
temp  
Kozlov Dmitry 已提交
203 204

	__rad_req_send(rpd->acct_req);
205 206 207 208 209 210
	/* The above call may set rpd->acct_req to NULL in the following chain of events:
	   1. __rad_req_send fails (on rad_server_realloc) and calls ppp_terminate;
	   2. As a result, an EV_PPP_FINISHING event is fired;
	   3. ppp_finishing calls rad_acct_stop that cleans up the request. */
	if (!rpd->acct_req)
		return;
K
temp  
Kozlov Dmitry 已提交
211

K
Kozlov Dmitry 已提交
212
	__sync_add_and_fetch(&rpd->acct_req->serv->stat_interim_sent, 1);
213

214
	rpd->acct_req->timeout.period = conf_timeout * 1000;
215
	triton_timer_add(rpd->ppp->ctrl->ctx, &rpd->acct_req->timeout, 0);
216 217 218 219
}

int rad_acct_start(struct radius_pd_t *rpd)
{
220
	int i;
221
	time_t ts;
222
	unsigned int dt;
K
temp  
Kozlov Dmitry 已提交
223
	
K
Kozlov Dmitry 已提交
224
	if (!conf_accounting)
225 226
		return 0;

227
	rpd->acct_req = rad_req_alloc(rpd, CODE_ACCOUNTING_REQUEST, rpd->ppp->username);
228
	if (!rpd->acct_req)
229 230 231
		return -1;

	if (rad_req_acct_fill(rpd->acct_req)) {
232
		log_ppp_error("radius:acct: failed to fill accounting attributes\n");
233 234 235 236 237 238 239 240
		goto out_err;
	}

	//if (rad_req_add_val(rpd->acct_req, "Acct-Status-Type", "Start", 4))
	//	goto out_err;
	//if (rad_req_add_str(rpd->acct_req, "Acct-Session-Id", rpd->ppp->sessionid, PPP_SESSIONID_LEN, 1))
	//	goto out_err;

241 242 243 244 245
	if (rpd->acct_req->reply) {
		rad_packet_free(rpd->acct_req->reply);
		rpd->acct_req->reply = NULL;
	}

246
	time(&rpd->acct_timestamp);
247
	
248
	if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret))
249 250
		goto out_err;

K
temp  
Kozlov Dmitry 已提交
251 252 253
	while (1) {

		if (rad_server_req_enter(rpd->acct_req)) {
254
			if (rad_server_realloc(rpd->acct_req)) {
K
temp  
Kozlov Dmitry 已提交
255
				log_ppp_warn("radius:acct_start: no servers available\n");
256
				goto out_err;
K
temp  
Kozlov Dmitry 已提交
257
			}
258
			if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret))
K
temp  
Kozlov Dmitry 已提交
259 260
				goto out_err;
			continue;
261
		}
K
temp  
Kozlov Dmitry 已提交
262 263 264 265 266

		for (i = 0; i < conf_max_try; i++) {
			if (conf_acct_delay_time) {
				time(&ts);
				rad_packet_change_int(rpd->acct_req->pack, NULL, "Acct-Delay-Time", ts - rpd->acct_timestamp);
267 268
				if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret)) {
					rad_server_req_exit(rpd->acct_req);
K
temp  
Kozlov Dmitry 已提交
269
					goto out_err;
270
				}
K
temp  
Kozlov Dmitry 已提交
271 272 273
			}

			if (rad_req_send(rpd->acct_req, conf_verbose))
274
				goto out;
K
temp  
Kozlov Dmitry 已提交
275

K
Kozlov Dmitry 已提交
276
			__sync_add_and_fetch(&rpd->acct_req->serv->stat_acct_sent, 1);
K
temp  
Kozlov Dmitry 已提交
277 278 279 280 281 282

			rad_req_wait(rpd->acct_req, conf_timeout);

			if (!rpd->acct_req->reply) {
				if (conf_acct_delay_time)
					rpd->acct_req->pack->id++;
K
Kozlov Dmitry 已提交
283 284 285
				__sync_add_and_fetch(&rpd->acct_req->serv->stat_acct_lost, 1);
				stat_accm_add(rpd->acct_req->serv->stat_acct_lost_1m, 1);
				stat_accm_add(rpd->acct_req->serv->stat_acct_lost_5m, 1);
K
temp  
Kozlov Dmitry 已提交
286 287 288 289
				continue;
			}

			dt = (rpd->acct_req->reply->tv.tv_sec - rpd->acct_req->pack->tv.tv_sec) * 1000 + 
290
				(rpd->acct_req->reply->tv.tv_nsec - rpd->acct_req->pack->tv.tv_nsec) / 1000000;
K
Kozlov Dmitry 已提交
291 292
			stat_accm_add(rpd->acct_req->serv->stat_acct_query_1m, dt);
			stat_accm_add(rpd->acct_req->serv->stat_acct_query_5m, dt);
K
temp  
Kozlov Dmitry 已提交
293 294 295 296

			if (rpd->acct_req->reply->id != rpd->acct_req->pack->id || rpd->acct_req->reply->code != CODE_ACCOUNTING_RESPONSE) {
				rad_packet_free(rpd->acct_req->reply);
				rpd->acct_req->reply = NULL;
297
				rpd->acct_req->pack->id++;
K
Kozlov Dmitry 已提交
298 299 300
				__sync_add_and_fetch(&rpd->acct_req->serv->stat_acct_lost, 1);
				stat_accm_add(rpd->acct_req->serv->stat_acct_lost_1m, 1);
				stat_accm_add(rpd->acct_req->serv->stat_acct_lost_5m, 1);
K
temp  
Kozlov Dmitry 已提交
301 302
			} else
				break;
303
		}
304

305
out:
K
temp  
Kozlov Dmitry 已提交
306
		rad_server_req_exit(rpd->acct_req);
307

308
		if (rpd->acct_req->reply)
309
			break;
310 311

		rad_server_fail(rpd->acct_req->serv);
312
		if (rad_server_realloc(rpd->acct_req)) {
313 314 315
			log_ppp_warn("radius:acct_start: no servers available\n");
			goto out_err;
		}
316
		if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret))
317
			goto out_err;
318 319
	}

320 321 322 323 324 325 326 327 328 329
	rpd->acct_req->hnd.read = rad_acct_read;

	triton_md_register_handler(rpd->ppp->ctrl->ctx, &rpd->acct_req->hnd);
	if (triton_md_enable_handler(&rpd->acct_req->hnd, MD_MODE_READ))
		goto out_err;
	
	rpd->acct_req->timeout.expire = rad_acct_timeout;
	rpd->acct_req->timeout.period = conf_timeout * 1000;
	
	rpd->acct_interim_timer.expire = rad_acct_interim_update;
330
	rpd->acct_interim_timer.period = rpd->acct_interim_interval ? rpd->acct_interim_interval * 1000 : STAT_UPDATE_INTERVAL;
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
	if (rpd->acct_interim_interval && triton_timer_add(rpd->ppp->ctrl->ctx, &rpd->acct_interim_timer, 0)) {
		triton_md_unregister_handler(&rpd->acct_req->hnd);
		triton_timer_del(&rpd->acct_req->timeout);
		goto out_err;
	}
	return 0;

out_err:
	rad_req_free(rpd->acct_req);
	rpd->acct_req = NULL;
	return -1;
}

void rad_acct_stop(struct radius_pd_t *rpd)
{
	int i;
347
	time_t ts;
348
	unsigned int dt;
349

K
Kozlov Dmitry 已提交
350
	if (!rpd->acct_req || !rpd->acct_req->serv)
351 352
		return;

353
	if (rpd->acct_interim_timer.tpd)
354 355 356 357
		triton_timer_del(&rpd->acct_interim_timer);

	if (rpd->acct_req) {
		triton_md_unregister_handler(&rpd->acct_req->hnd);
358
		if (rpd->acct_req->timeout.tpd)
359 360
			triton_timer_del(&rpd->acct_req->timeout);

361 362
		switch (rpd->ppp->terminate_cause) {
			case TERM_USER_REQUEST:
K
Kozlov Dmitry 已提交
363
				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "User-Request");
364 365
				break;
			case TERM_SESSION_TIMEOUT:
K
Kozlov Dmitry 已提交
366
				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Session-Timeout");
367 368
				break;
			case TERM_ADMIN_RESET:
K
Kozlov Dmitry 已提交
369
				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Admin-Reset");
370 371 372
				break;
			case TERM_USER_ERROR:
			case TERM_AUTH_ERROR:
K
Kozlov Dmitry 已提交
373
				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "User-Error");
374 375
				break;
			case TERM_NAS_ERROR:
K
Kozlov Dmitry 已提交
376
				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "NAS-Error");
377
				break;
378
			case TERM_NAS_REQUEST:
K
Kozlov Dmitry 已提交
379
				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "NAS-Request");
380
				break;
381
			case TERM_NAS_REBOOT:
K
Kozlov Dmitry 已提交
382
				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "NAS-Reboot");
383
				break;
384 385 386
			case TERM_LOST_CARRIER:
				rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Lost-Carrier");
				break;
387
		}
K
Kozlov Dmitry 已提交
388
		rad_packet_change_val(rpd->acct_req->pack, NULL, "Acct-Status-Type", "Stop");
389
		req_set_stat(rpd->acct_req, rpd->ppp);
390
		req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret);
391
		/// !!! rad_req_add_val(rpd->acct_req, "Acct-Terminate-Cause", "");
D
Dmitry Kozlov 已提交
392 393 394 395 396
		
		if (rpd->acct_req->reply) {
			rad_packet_free(rpd->acct_req->reply);
			rpd->acct_req->reply = NULL;
		}
397
	
398
		time(&rpd->acct_timestamp);
D
Dmitry Kozlov 已提交
399

K
temp  
Kozlov Dmitry 已提交
400 401 402
		while (1) {

			if (rad_server_req_enter(rpd->acct_req)) {
403
				if (rad_server_realloc(rpd->acct_req)) {
K
temp  
Kozlov Dmitry 已提交
404
					log_ppp_warn("radius:acct_stop: no servers available\n");
405
					break;
K
temp  
Kozlov Dmitry 已提交
406
				}
407
				req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret);
408
				continue;
D
Dmitry Kozlov 已提交
409
			}
410

K
temp  
Kozlov Dmitry 已提交
411 412 413 414 415
			for(i = 0; i < conf_max_try; i++) {
				if (conf_acct_delay_time) {
					time(&ts);
					rad_packet_change_int(rpd->acct_req->pack, NULL, "Acct-Delay-Time", ts - rpd->acct_timestamp);
					rpd->acct_req->pack->id++;
416
					if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret))
K
temp  
Kozlov Dmitry 已提交
417 418 419
						break;
				}
				if (rad_req_send(rpd->acct_req, conf_verbose))
420
					goto out;
K
Kozlov Dmitry 已提交
421
				__sync_add_and_fetch(&rpd->acct_req->serv->stat_acct_sent, 1);
K
temp  
Kozlov Dmitry 已提交
422 423
				rad_req_wait(rpd->acct_req, conf_timeout);
				if (!rpd->acct_req->reply) {
K
Kozlov Dmitry 已提交
424 425 426
					__sync_add_and_fetch(&rpd->acct_req->serv->stat_acct_lost, 1);
					stat_accm_add(rpd->acct_req->serv->stat_acct_lost_1m, 1);
					stat_accm_add(rpd->acct_req->serv->stat_acct_lost_5m, 1);
K
temp  
Kozlov Dmitry 已提交
427 428 429 430
					continue;
				}

				dt = (rpd->acct_req->reply->tv.tv_sec - rpd->acct_req->pack->tv.tv_sec) * 1000 + 
431
					(rpd->acct_req->reply->tv.tv_nsec - rpd->acct_req->pack->tv.tv_nsec) / 1000000;
K
Kozlov Dmitry 已提交
432 433
				stat_accm_add(rpd->acct_req->serv->stat_acct_query_1m, dt);
				stat_accm_add(rpd->acct_req->serv->stat_acct_query_5m, dt);
K
temp  
Kozlov Dmitry 已提交
434 435 436 437

				if (rpd->acct_req->reply->id != rpd->acct_req->pack->id || rpd->acct_req->reply->code != CODE_ACCOUNTING_RESPONSE) {
					rad_packet_free(rpd->acct_req->reply);
					rpd->acct_req->reply = NULL;
K
Kozlov Dmitry 已提交
438 439 440
					__sync_add_and_fetch(&rpd->acct_req->serv->stat_acct_lost, 1);
					stat_accm_add(rpd->acct_req->serv->stat_acct_lost_1m, 1);
					stat_accm_add(rpd->acct_req->serv->stat_acct_lost_5m, 1);
K
temp  
Kozlov Dmitry 已提交
441 442 443
				} else
					break;
			}
444

445
out:
K
temp  
Kozlov Dmitry 已提交
446 447
			rad_server_req_exit(rpd->acct_req);

K
Kozlov Dmitry 已提交
448 449 450 451
			if (rpd->acct_req->reply)
				break;

			rad_server_fail(rpd->acct_req->serv);
452
			if (rad_server_realloc(rpd->acct_req)) {
K
Kozlov Dmitry 已提交
453 454
				log_ppp_warn("radius:acct_stop: no servers available\n");
				break;
K
temp  
Kozlov Dmitry 已提交
455
			}
456
			req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret);
457 458 459 460 461 462 463
		}

		rad_req_free(rpd->acct_req);
		rpd->acct_req = NULL;
	}
}