acct.c 12.8 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
		req->reply = NULL;
	}

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

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

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

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

104 105 106 107 108
	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);

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

	return 0;
}

K
temp  
Kozlov Dmitry 已提交
120 121 122 123
static void __rad_req_send(struct rad_req_t *req)
{
	while (1) {
		if (rad_server_req_enter(req)) {
124
			if (rad_server_realloc(req)) {
K
temp  
Kozlov Dmitry 已提交
125
				if (conf_acct_timeout) {
126
					log_ppp_warn("radius:acct: no servers available, terminating session...\n");
K
temp  
Kozlov Dmitry 已提交
127 128 129 130 131 132 133 134
					ppp_terminate(req->rpd->ppp, TERM_NAS_ERROR, 0);
				}
				break;
			}
			continue;
		}

		rad_req_send(req, conf_interim_verbose);
135 136 137 138
		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 已提交
139 140 141 142 143 144 145

		rad_server_req_exit(req);

		break;
	}
}

146 147 148
static void rad_acct_timeout(struct triton_timer_t *t)
{
	struct rad_req_t *req = container_of(t, typeof(*req), timeout);
149 150
	time_t ts, dt;

D
Dmitry Kozlov 已提交
151
	__sync_add_and_fetch(&stat_interim_lost, 1);
152 153
	stat_accm_add(stat_interim_lost_1m, 1);
	stat_accm_add(stat_interim_lost_5m, 1);
154 155 156 157 158 159 160

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

161 162 163 164 165
	time(&ts);

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

	if (dt > conf_acct_timeout) {
K
temp  
Kozlov Dmitry 已提交
166
		rad_server_fail(req->serv);
167
		if (rad_server_realloc(req)) {
K
temp  
Kozlov Dmitry 已提交
168 169 170 171 172
			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);
173 174 175 176 177 178 179 180 181 182
	}
	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);
		}
	}
183

184 185 186
	if (conf_acct_delay_time) {
		req->pack->id++;	
		rad_packet_change_int(req->pack, NULL, "Acct-Delay-Time", dt);
K
temp  
Kozlov Dmitry 已提交
187
		req_set_RA(req, req->serv->acct_secret);
188 189
	}

K
temp  
Kozlov Dmitry 已提交
190 191
	__rad_req_send(req);

D
Dmitry Kozlov 已提交
192
	__sync_add_and_fetch(&stat_interim_sent, 1);
193 194 195 196 197 198
}

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

199
	if (rpd->acct_req->timeout.tpd)
200 201
		return;

202 203 204 205
	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;

206
	req_set_stat(rpd->acct_req, rpd->ppp);
207 208 209
	if (!rpd->acct_interim_interval)
		return;

210
	time(&rpd->acct_timestamp);
211 212
	rpd->acct_req->pack->id++;

K
Kozlov Dmitry 已提交
213
	rad_packet_change_val(rpd->acct_req->pack, NULL, "Acct-Status-Type", "Interim-Update");
214 215
	if (conf_acct_delay_time)
		rad_packet_change_int(rpd->acct_req->pack, NULL, "Acct-Delay-Time", 0);
K
temp  
Kozlov Dmitry 已提交
216 217 218 219
	req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret);

	__rad_req_send(rpd->acct_req);

D
Dmitry Kozlov 已提交
220
	__sync_add_and_fetch(&stat_interim_sent, 1);
221 222

	triton_timer_add(rpd->ppp->ctrl->ctx, &rpd->acct_req->timeout, 0);
223 224 225 226
}

int rad_acct_start(struct radius_pd_t *rpd)
{
227
	int i;
228
	time_t ts;
229
	unsigned int dt;
K
temp  
Kozlov Dmitry 已提交
230
	
K
Kozlov Dmitry 已提交
231
	if (!conf_accounting)
232 233
		return 0;

234 235
	rpd->acct_req = rad_req_alloc(rpd, CODE_ACCOUNTING_REQUEST, rpd->ppp->username);
	if (!rpd->acct_req) {
236
		log_emerg("radius: out of memory\n");
237 238 239 240
		return -1;
	}

	if (rad_req_acct_fill(rpd->acct_req)) {
241
		log_ppp_error("radius:acct: failed to fill accounting attributes\n");
242 243 244 245 246 247 248 249
		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;

250 251 252 253 254
	if (rpd->acct_req->reply) {
		rad_packet_free(rpd->acct_req->reply);
		rpd->acct_req->reply = NULL;
	}

255
	time(&rpd->acct_timestamp);
256
	
K
temp  
Kozlov Dmitry 已提交
257
	if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret))
258 259
		goto out_err;

K
temp  
Kozlov Dmitry 已提交
260 261 262
	while (1) {

		if (rad_server_req_enter(rpd->acct_req)) {
263
			if (rad_server_realloc(rpd->acct_req)) {
K
temp  
Kozlov Dmitry 已提交
264
				log_ppp_warn("radius:acct_start: no servers available\n");
265
				goto out_err;
K
temp  
Kozlov Dmitry 已提交
266 267 268 269
			}
			if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret))
				goto out_err;
			continue;
270
		}
K
temp  
Kozlov Dmitry 已提交
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

		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;
304
				rpd->acct_req->pack->id++;
K
temp  
Kozlov Dmitry 已提交
305 306 307 308 309
				__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;
310
		}
311

K
temp  
Kozlov Dmitry 已提交
312
		rad_server_req_exit(rpd->acct_req);
313

314
		if (rpd->acct_req->reply)
315
			break;
316 317

		rad_server_fail(rpd->acct_req->serv);
318
		if (rad_server_realloc(rpd->acct_req)) {
319 320 321 322 323
			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;
324 325
	}

326 327 328 329 330 331 332 333 334 335
	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;
336
	rpd->acct_interim_timer.period = rpd->acct_interim_interval ? rpd->acct_interim_interval * 1000 : STAT_UPDATE_INTERVAL;
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
	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;
353
	time_t ts;
354
	unsigned int dt;
355

K
Kozlov Dmitry 已提交
356
	if (!rpd->acct_req || !rpd->acct_req->serv)
357 358
		return;

359
	if (rpd->acct_interim_timer.tpd)
360 361 362 363
		triton_timer_del(&rpd->acct_interim_timer);

	if (rpd->acct_req) {
		triton_md_unregister_handler(&rpd->acct_req->hnd);
364
		if (rpd->acct_req->timeout.tpd)
365 366
			triton_timer_del(&rpd->acct_req->timeout);

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

K
temp  
Kozlov Dmitry 已提交
406 407 408
		while (1) {

			if (rad_server_req_enter(rpd->acct_req)) {
409
				if (rad_server_realloc(rpd->acct_req)) {
K
temp  
Kozlov Dmitry 已提交
410
					log_ppp_warn("radius:acct_stop: no servers available\n");
411
					break;
K
temp  
Kozlov Dmitry 已提交
412 413
				}
				req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret);
414
				continue;
D
Dmitry Kozlov 已提交
415
			}
416

K
temp  
Kozlov Dmitry 已提交
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 443 444 445 446 447 448 449
			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;
			}
450

K
temp  
Kozlov Dmitry 已提交
451 452
			rad_server_req_exit(rpd->acct_req);

K
Kozlov Dmitry 已提交
453 454 455 456
			if (rpd->acct_req->reply)
				break;

			rad_server_fail(rpd->acct_req->serv);
457
			if (rad_server_realloc(rpd->acct_req)) {
K
Kozlov Dmitry 已提交
458 459
				log_ppp_warn("radius:acct_stop: no servers available\n");
				break;
K
temp  
Kozlov Dmitry 已提交
460
			}
K
Kozlov Dmitry 已提交
461
			req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret);
462 463 464 465 466 467 468
		}

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