acct.c 12.7 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 11 12
#include <openssl/md5.h>

#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 37
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)
{
	struct ifpppstatsreq ifreq;
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

	memset(&ifreq, 0, sizeof(ifreq));
	ifreq.stats_ptr = (void *)&ifreq.stats;
47
	strcpy(ifreq.ifr__name, ppp->ifname);
48 49

	if (ioctl(sock_fd, SIOCGPPPSTATS, &ifreq)) {
50
		log_ppp_error("radius: failed to get ppp statistics: %s\n", strerror(errno));
51 52 53
		return;
	}

54 55 56 57 58 59 60 61
	if (ifreq.stats.p.ppp_ibytes < req->rpd->acct_input_octets)
		req->rpd->acct_input_gigawords++;
	req->rpd->acct_input_octets = ifreq.stats.p.ppp_ibytes;

	if (ifreq.stats.p.ppp_obytes < req->rpd->acct_output_octets)
		req->rpd->acct_output_gigawords++;
	req->rpd->acct_output_octets = ifreq.stats.p.ppp_obytes;

K
Kozlov Dmitry 已提交
62 63 64 65 66 67 68
	rad_packet_change_int(req->pack, NULL, "Acct-Input-Octets", ifreq.stats.p.ppp_ibytes);
	rad_packet_change_int(req->pack, NULL, "Acct-Output-Octets", ifreq.stats.p.ppp_obytes);
	rad_packet_change_int(req->pack, NULL, "Acct-Input-Packets", ifreq.stats.p.ppp_ipackets);
	rad_packet_change_int(req->pack, NULL, "Acct-Output-Packets", ifreq.stats.p.ppp_opackets);
	rad_packet_change_int(req->pack, NULL, "Acct-Input-Gigawords", req->rpd->acct_input_gigawords);
	rad_packet_change_int(req->pack, NULL, "Acct-Output-Gigawords", req->rpd->acct_output_gigawords);
	rad_packet_change_int(req->pack, NULL, "Acct-Session-Time", stop_time - ppp->start_time);
69 70 71 72 73
}

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 已提交
74 75
	struct rad_packet_t *pack;
	int r;
76
	unsigned int dt;
77

K
Kozlov Dmitry 已提交
78
	if (req->reply) {
D
Dmitry Kozlov 已提交
79
		rad_packet_free(req->reply);
K
Kozlov Dmitry 已提交
80 81 82 83 84 85 86 87 88 89
		req->reply = NULL;
	}

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

		if (pack) {
			if (req->reply)
				rad_packet_free(req->reply);
			req->reply = pack;
90
			if (conf_interim_verbose) {
91 92
				log_ppp_info2("recv ");
				rad_packet_print(req->reply, log_ppp_info2);
K
Kozlov Dmitry 已提交
93 94 95 96 97 98
			}
		}

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

100 101 102
	if (!req->reply)
		return 0;

103 104 105 106 107
	dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 + 
		(req->reply->tv.tv_usec - req->pack->tv.tv_usec) / 1000;
	stat_accm_add(stat_interim_query_1m, dt);
	stat_accm_add(stat_interim_query_5m, dt);

108 109 110 111
	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 已提交
112 113
		if (req->timeout.tpd)
			triton_timer_del(&req->timeout);
114 115 116 117 118
	}

	return 0;
}

K
temp  
Kozlov Dmitry 已提交
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
static void __rad_req_send(struct rad_req_t *req)
{
	while (1) {

		if (rad_server_req_enter(req)) {
			if (rad_server_realloc(req, 1)) {
				log_ppp_warn("radius:acct: no servers available, terminating session...\n");
				if (conf_acct_timeout) {
					ppp_terminate(req->rpd->ppp, TERM_NAS_ERROR, 0);
					return;
				}
				break;
			}
			continue;
		}

		rad_req_send(req, conf_interim_verbose);

		rad_server_req_exit(req);

		break;
	}
}

143 144 145
static void rad_acct_timeout(struct triton_timer_t *t)
{
	struct rad_req_t *req = container_of(t, typeof(*req), timeout);
146 147
	time_t ts, dt;

D
Dmitry Kozlov 已提交
148
	__sync_add_and_fetch(&stat_interim_lost, 1);
149 150
	stat_accm_add(stat_interim_lost_1m, 1);
	stat_accm_add(stat_interim_lost_5m, 1);
D
Dmitry Kozlov 已提交
151
	
152 153 154 155 156
	time(&ts);

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

	if (dt > conf_acct_timeout) {
K
temp  
Kozlov Dmitry 已提交
157 158 159 160 161 162 163
		rad_server_fail(req->serv);
		if (rad_server_realloc(req, 1)) {
			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);
164 165 166 167 168 169 170 171 172 173
	}
	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);
		}
	}
174

175 176 177
	if (conf_acct_delay_time) {
		req->pack->id++;	
		rad_packet_change_int(req->pack, NULL, "Acct-Delay-Time", dt);
K
temp  
Kozlov Dmitry 已提交
178
		req_set_RA(req, req->serv->acct_secret);
179 180
	}

K
temp  
Kozlov Dmitry 已提交
181 182
	__rad_req_send(req);

D
Dmitry Kozlov 已提交
183
	__sync_add_and_fetch(&stat_interim_sent, 1);
184 185 186 187 188 189
}

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

190
	if (rpd->acct_req->timeout.tpd)
191 192
		return;

193 194 195 196
	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;

197
	req_set_stat(rpd->acct_req, rpd->ppp);
198 199 200
	if (!rpd->acct_interim_interval)
		return;

201
	time(&rpd->acct_timestamp);
202 203
	rpd->acct_req->pack->id++;

K
Kozlov Dmitry 已提交
204
	rad_packet_change_val(rpd->acct_req->pack, NULL, "Acct-Status-Type", "Interim-Update");
205 206
	if (conf_acct_delay_time)
		rad_packet_change_int(rpd->acct_req->pack, NULL, "Acct-Delay-Time", 0);
K
temp  
Kozlov Dmitry 已提交
207 208 209 210
	req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret);

	__rad_req_send(rpd->acct_req);

D
Dmitry Kozlov 已提交
211
	__sync_add_and_fetch(&stat_interim_sent, 1);
212
	if (conf_acct_timeout) {
213
		rpd->acct_req->timeout.period = conf_timeout * 1000;
214 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 228
	rpd->acct_req = rad_req_alloc(rpd, CODE_ACCOUNTING_REQUEST, rpd->ppp->username);
	if (!rpd->acct_req) {
229
		log_emerg("radius: out of memory\n");
230 231 232 233
		return -1;
	}

	if (rad_req_acct_fill(rpd->acct_req)) {
234
		log_ppp_error("radius:acct: failed to fill accounting attributes\n");
235 236 237 238 239 240 241 242
		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;

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

248
	time(&rpd->acct_timestamp);
249
	
K
temp  
Kozlov Dmitry 已提交
250
	if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret))
251 252
		goto out_err;

K
temp  
Kozlov Dmitry 已提交
253 254 255 256 257
	while (1) {

		if (rad_server_req_enter(rpd->acct_req)) {
			if (rad_server_realloc(rpd->acct_req, 1)) {
				log_ppp_warn("radius:acct_start: no servers available\n");
258
				goto out_err;
K
temp  
Kozlov Dmitry 已提交
259 260 261 262
			}
			if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret))
				goto out_err;
			continue;
263
		}
K
temp  
Kozlov Dmitry 已提交
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

		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);
				if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret))
					goto out_err;
			}

			if (rad_req_send(rpd->acct_req, conf_verbose))
				goto out_err;

			__sync_add_and_fetch(&stat_acct_sent, 1);

			rad_req_wait(rpd->acct_req, conf_timeout);

			if (!rpd->acct_req->reply) {
				if (conf_acct_delay_time)
					rpd->acct_req->pack->id++;
				__sync_add_and_fetch(&stat_acct_lost, 1);
				stat_accm_add(stat_acct_lost_1m, 1);
				stat_accm_add(stat_acct_lost_5m, 1);
				continue;
			}

			dt = (rpd->acct_req->reply->tv.tv_sec - rpd->acct_req->pack->tv.tv_sec) * 1000 + 
				(rpd->acct_req->reply->tv.tv_usec - rpd->acct_req->pack->tv.tv_usec) / 1000;
			stat_accm_add(stat_acct_query_1m, dt);
			stat_accm_add(stat_acct_query_5m, dt);

			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
temp  
Kozlov Dmitry 已提交
298 299 300 301 302
				__sync_add_and_fetch(&stat_acct_lost, 1);
				stat_accm_add(stat_acct_lost_1m, 1);
				stat_accm_add(stat_acct_lost_5m, 1);
			} else
				break;
303
		}
304

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

307
		if (rpd->acct_req->reply)
308
			break;
309 310 311 312 313 314 315 316

		rad_server_fail(rpd->acct_req->serv);
		if (rad_server_realloc(rpd->acct_req, 1)) {
			log_ppp_warn("radius:acct_start: no servers available\n");
			goto out_err;
		}
		if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret))
			goto out_err;
317 318
	}

319 320 321 322 323 324 325 326 327 328
	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;
329
	rpd->acct_interim_timer.period = rpd->acct_interim_interval ? rpd->acct_interim_interval * 1000 : STAT_UPDATE_INTERVAL;
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
	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;
346
	time_t ts;
347
	unsigned int dt;
348

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

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

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

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

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

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

K
temp  
Kozlov Dmitry 已提交
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
			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++;
					if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret))
						break;
				}
				if (rad_req_send(rpd->acct_req, conf_verbose))
					break;
				__sync_add_and_fetch(&stat_acct_sent, 1);
				rad_req_wait(rpd->acct_req, conf_timeout);
				if (!rpd->acct_req->reply) {
					__sync_add_and_fetch(&stat_acct_lost, 1);
					stat_accm_add(stat_acct_lost_1m, 1);
					stat_accm_add(stat_acct_lost_5m, 1);
					continue;
				}

				dt = (rpd->acct_req->reply->tv.tv_sec - rpd->acct_req->pack->tv.tv_sec) * 1000 + 
					(rpd->acct_req->reply->tv.tv_usec - rpd->acct_req->pack->tv.tv_usec) / 1000;
				stat_accm_add(stat_acct_query_1m, dt);
				stat_accm_add(stat_acct_query_5m, dt);

				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;
					__sync_add_and_fetch(&stat_acct_lost, 1);
					stat_accm_add(stat_acct_lost_1m, 1);
					stat_accm_add(stat_acct_lost_5m, 1);
				} else
					break;
			}
443

K
temp  
Kozlov Dmitry 已提交
444 445
			rad_server_req_exit(rpd->acct_req);

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

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

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