From 62e89248160d3592c2d754fcaa15e37586a5b091 Mon Sep 17 00:00:00 2001 From: Dmitry Kozlov Date: Sat, 20 Sep 2014 12:18:49 +0400 Subject: rewrite of authentication/accounting procedures This patch gets rid of synchronuos style of authentication/accounting. Synchronous style of authentication/accounting produced sleeping threads which becomes a problem when lots of sessions started/stopped and all they want authorization/accounting. --- accel-pppd/radius/acct.c | 654 +++++++++++++++++++++++------------------------ 1 file changed, 315 insertions(+), 339 deletions(-) (limited to 'accel-pppd/radius/acct.c') diff --git a/accel-pppd/radius/acct.c b/accel-pppd/radius/acct.c index 9a5e549e..f9919453 100644 --- a/accel-pppd/radius/acct.c +++ b/accel-pppd/radius/acct.c @@ -18,7 +18,6 @@ #include "memdebug.h" -#define STAT_UPDATE_INTERVAL (10 * 60 * 1000) #define INTERIM_SAFE_TIME 10 static int req_set_RA(struct rad_req_t *req, const char *secret) @@ -40,13 +39,13 @@ static int req_set_stat(struct rad_req_t *req, struct ap_session *ses) { struct rtnl_link_stats stats; struct radius_pd_t *rpd = req->rpd; - time_t stop_time; + struct timespec ts; int ret = 0; if (ses->stop_time) - stop_time = ses->stop_time; + ts.tv_sec = ses->stop_time; else - time(&stop_time); + clock_gettime(CLOCK_MONOTONIC, &ts); if (ap_session_read_stats(ses, &stats) == 0) { rad_packet_change_int(req->pack, NULL, "Acct-Input-Octets", stats.rx_bytes); @@ -58,99 +57,56 @@ static int req_set_stat(struct rad_req_t *req, struct ap_session *ses) } else ret = -1; - rad_packet_change_int(req->pack, NULL, "Acct-Session-Time", stop_time - ses->start_time); + rad_packet_change_int(req->pack, NULL, "Acct-Session-Time", ts.tv_sec - ses->start_time); return ret; } -static int rad_acct_read(struct triton_md_handler_t *h) +static void rad_acct_sent(struct rad_req_t *req, int res) { - struct rad_req_t *req = container_of(h, typeof(*req), hnd); - struct rad_packet_t *pack; - int r; - unsigned int dt; - - if (req->reply) { - rad_packet_free(req->reply); - req->reply = NULL; + if (res) { + if (conf_acct_timeout) + ap_session_terminate(req->rpd->ses, TERM_NAS_ERROR, 0); + else + triton_timer_del(&req->timeout); + return; } - - while (1) { - r = rad_packet_recv(h->fd, &pack, NULL); - - if (pack) { - rad_server_reply(req->serv); - if (req->reply) - rad_packet_free(req->reply); - req->reply = pack; - if (conf_interim_verbose) { - log_ppp_info2("recv "); - rad_packet_print(req->reply, req->serv, log_ppp_info2); - } - } - - if (r) - break; + + __sync_add_and_fetch(&req->serv->stat_interim_sent, 1); + + if (!req->hnd.tpd) { + triton_md_register_handler(req->rpd->ses->ctrl->ctx, &req->hnd); + triton_md_enable_handler(&req->hnd, MD_MODE_READ); } - if (!req->reply) - return 0; - - if (req->reply->id != req->pack->id) - return 0; - - rad_server_req_exit(req); + if (req->timeout.tpd) + triton_timer_mod(&req->timeout, 0); + else + triton_timer_add(req->rpd->ses->ctrl->ctx, &req->timeout, 0); +} - dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 + +static void rad_acct_recv(struct rad_req_t *req) +{ + int dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 + (req->reply->tv.tv_nsec - req->pack->tv.tv_nsec) / 1000000; stat_accm_add(req->serv->stat_interim_query_1m, dt); stat_accm_add(req->serv->stat_interim_query_5m, dt); - if (req->reply->code != CODE_ACCOUNTING_RESPONSE || req->reply->id != req->pack->id) { - rad_packet_free(req->reply); - req->reply = NULL; - } else { - if (req->timeout.tpd) - triton_timer_del(&req->timeout); - } - - triton_md_unregister_handler(h, 1); - - return 1; -} - -static int __rad_req_send(struct rad_req_t *req) -{ - while (1) { - if (rad_server_req_enter(req)) { - if (rad_server_realloc(req)) - return -1; - continue; - } - - if (rad_req_send(req, conf_interim_verbose ? log_ppp_info2 : NULL)) { - rad_server_req_exit(req); - rad_server_fail(req->serv); - continue; - } - - if (!req->hnd.tpd) { - triton_md_register_handler(req->rpd->ses->ctrl->ctx, &req->hnd); - triton_md_enable_handler(&req->hnd, MD_MODE_READ); - } + triton_timer_del(&req->timeout); - break; - } - - return 0; + triton_md_unregister_handler(&req->hnd, 1); + + rad_packet_free(req->reply); + req->reply = NULL; } static void rad_acct_timeout(struct triton_timer_t *t) { struct rad_req_t *req = container_of(t, typeof(*req), timeout); - time_t ts, dt; - + time_t dt; + struct timespec ts; + rad_server_req_exit(req); rad_server_timeout(req->serv); @@ -164,9 +120,9 @@ static void rad_acct_timeout(struct triton_timer_t *t) return; } - time(&ts); + clock_gettime(CLOCK_MONOTONIC, &ts); - dt = ts - req->rpd->acct_timestamp; + dt = ts.tv_sec - req->ts; if (dt > conf_acct_timeout) { rad_server_fail(req->serv); @@ -176,42 +132,30 @@ static void rad_acct_timeout(struct triton_timer_t *t) ap_session_terminate(req->rpd->ses, TERM_NAS_ERROR, 0); return; } - time(&req->rpd->acct_timestamp); } - 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); - } + if (dt > conf_acct_timeout / 2) + req->timeout.expire_tv.tv_sec++; + else if (dt > conf_acct_timeout / 4) { + if (req->timeout.expire_tv.tv_sec < conf_timeout * 2) + req->timeout.expire_tv.tv_sec = conf_timeout * 2; } - if (conf_acct_delay_time) { + if (conf_acct_delay_time) req->pack->id++; - rad_packet_change_int(req->pack, NULL, "Acct-Delay-Time", dt); - req_set_RA(req, req->serv->secret); - } - - if (__rad_req_send(req)) { - triton_timer_del(t); - - if (conf_acct_timeout) { - log_ppp_warn("radius:acct: no servers available, terminating session...\n"); - ap_session_terminate(req->rpd->ses, TERM_NAS_ERROR, 0); - } + + req->try = 0; - return; + if (rad_req_send(req) && conf_acct_timeout) { + log_ppp_warn("radius:acct: no servers available, terminating session...\n"); + ap_session_terminate(req->rpd->ses, TERM_NAS_ERROR, 0); } - - __sync_add_and_fetch(&req->serv->stat_interim_sent, 1); } static void rad_acct_interim_update(struct triton_timer_t *t) { struct radius_pd_t *rpd = container_of(t, typeof(*rpd), acct_interim_timer); + struct timespec ts; if (rpd->acct_req->timeout.tpd) return; @@ -228,281 +172,313 @@ static void rad_acct_interim_update(struct triton_timer_t *t) if (!rpd->acct_interim_interval) return; - time(&rpd->acct_timestamp); + clock_gettime(CLOCK_MONOTONIC, &ts); + rpd->acct_req->ts = ts.tv_sec; rpd->acct_req->pack->id++; - rad_packet_change_val(rpd->acct_req->pack, NULL, "Acct-Status-Type", "Interim-Update"); - if (conf_acct_delay_time) - rad_packet_change_int(rpd->acct_req->pack, NULL, "Acct-Delay-Time", 0); - req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret); + if (!rpd->acct_req->before_send) + req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret); + + rpd->acct_req->timeout.expire_tv.tv_sec = conf_timeout; + rpd->acct_req->try = 0; - if (__rad_req_send(rpd->acct_req)) - return; + if (rad_req_send(rpd->acct_req) && conf_acct_timeout) { + log_ppp_warn("radius:acct: no servers available, terminating session...\n"); + ap_session_terminate(rpd->ses, TERM_NAS_ERROR, 0); + } +} - /* 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; +static int rad_acct_before_send(struct rad_req_t *req) +{ + struct timespec ts; - __sync_add_and_fetch(&rpd->acct_req->serv->stat_interim_sent, 1); + clock_gettime(CLOCK_MONOTONIC, &ts); + + rad_packet_change_int(req->pack, NULL, "Acct-Delay-Time", ts.tv_sec - req->ts); + req_set_RA(req, req->serv->secret); - rpd->acct_req->timeout.period = conf_timeout * 1000; - triton_timer_add(rpd->ses->ctrl->ctx, &rpd->acct_req->timeout, 0); + return 0; } -int rad_acct_start(struct radius_pd_t *rpd) +static void rad_acct_start_sent(struct rad_req_t *req, int res) { - int i; - time_t ts; - unsigned int dt; + if (res) { + ap_session_terminate(req->rpd->ses, TERM_NAS_ERROR, 0); + return; + } - if (!conf_accounting) - return 0; + __sync_add_and_fetch(&req->serv->stat_acct_sent, 1); - if (!rpd->acct_req) - rpd->acct_req = rad_req_alloc(rpd, CODE_ACCOUNTING_REQUEST, rpd->ses->username); + if (!req->hnd.tpd) { + triton_md_register_handler(req->rpd->ses->ctrl->ctx, &req->hnd); + triton_md_enable_handler(&req->hnd, MD_MODE_READ); + } - if (!rpd->acct_req) - return -1; + if (req->timeout.tpd) + triton_timer_mod(&req->timeout, 0); + else + triton_timer_add(req->rpd->ses->ctrl->ctx, &req->timeout, 0); +} - if (rad_req_acct_fill(rpd->acct_req)) { - log_ppp_error("radius:acct: failed to fill accounting attributes\n"); - goto out_err; - } +static void rad_acct_start_recv(struct rad_req_t *req) +{ + struct radius_pd_t *rpd = req->rpd; + int dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 + + (req->reply->tv.tv_nsec - req->pack->tv.tv_nsec) / 1000000; + + stat_accm_add(req->serv->stat_acct_query_1m, dt); + stat_accm_add(req->serv->stat_acct_query_5m, dt); - //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->ses->ionid, PPP_SESSIONID_LEN, 1)) - // goto out_err; + triton_timer_del(&req->timeout); - if (rpd->acct_req->reply) { - rad_packet_free(rpd->acct_req->reply); - rpd->acct_req->reply = NULL; + triton_md_unregister_handler(&req->hnd, 1); + + if (rpd->acct_interim_interval) { + rad_packet_free(req->reply); + req->reply = NULL; + + rad_packet_change_val(req->pack, NULL, "Acct-Status-Type", "Interim-Update"); + rpd->acct_interim_timer.expire = rad_acct_interim_update; + rpd->acct_interim_timer.period = rpd->acct_interim_interval * 1000; + triton_timer_add(rpd->ses->ctrl->ctx, &rpd->acct_interim_timer, 0); + + req->timeout.expire = rad_acct_timeout; + req->recv = rad_acct_recv; + req->sent = rad_acct_sent; + req->log = conf_interim_verbose ? log_ppp_info2 : NULL; + } else { + rad_req_free(rpd->acct_req); + rpd->acct_req = NULL; } - time(&rpd->acct_timestamp); + rpd->acct_started = 1; - if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret)) - goto out_err; + ap_session_accounting_started(rpd->ses); +} -#ifdef USE_BACKUP - if (rpd->ses->state != AP_STATE_RESTORE || !rpd->ses->backup->internal) { -#endif - while (1) { - - if (rad_server_req_enter(rpd->acct_req)) { - if (rad_server_realloc(rpd->acct_req)) { - log_ppp_warn("radius:acct_start: no servers available\n"); - goto out_err; - } - if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret)) - goto out_err; - continue; - } - - 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->secret)) { - rad_server_req_exit(rpd->acct_req); - goto out_err; - } - } - - if (rad_req_send(rpd->acct_req, conf_verbose ? log_ppp_info1 : NULL)) - goto out; - - __sync_add_and_fetch(&rpd->acct_req->serv->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(&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); - continue; - } - - dt = (rpd->acct_req->reply->tv.tv_sec - rpd->acct_req->pack->tv.tv_sec) * 1000 + - (rpd->acct_req->reply->tv.tv_nsec - rpd->acct_req->pack->tv.tv_nsec) / 1000000; - stat_accm_add(rpd->acct_req->serv->stat_acct_query_1m, dt); - stat_accm_add(rpd->acct_req->serv->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(&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); - } else - break; - } - -out: - rad_server_req_exit(rpd->acct_req); - - if (rpd->acct_req->reply) - break; - - rad_server_fail(rpd->acct_req->serv); - if (rad_server_realloc(rpd->acct_req)) { - log_ppp_warn("radius:acct_start: no servers available\n"); - goto out_err; - } - if (req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret)) - goto out_err; - } -#ifdef USE_BACKUP +static void rad_acct_start_timeout(struct triton_timer_t *t) +{ + struct rad_req_t *req = container_of(t, typeof(*req), timeout); + + __sync_add_and_fetch(&req->serv->stat_acct_lost, 1); + stat_accm_add(req->serv->stat_acct_lost_1m, 1); + stat_accm_add(req->serv->stat_acct_lost_5m, 1); + + if (req->before_send) + req->pack->id++; + + if (rad_req_send(req)) + ap_session_terminate(req->rpd->ses, TERM_NAS_ERROR, 0); +} + +int rad_acct_start(struct radius_pd_t *rpd) +{ + struct rad_req_t *req = rad_req_alloc(rpd, CODE_ACCOUNTING_REQUEST, rpd->ses->username); + + if (!req) + return -1; + + if (rad_req_acct_fill(req)) { + log_ppp_error("radius:acct: failed to fill accounting attributes\n"); + goto out_err; } -#endif - close(rpd->acct_req->hnd.fd); - rpd->acct_req->hnd.fd = -1; - rpd->acct_req->hnd.read = rad_acct_read; + if (conf_acct_delay_time) + req->before_send = rad_acct_before_send; + else if (req_set_RA(req, req->serv->secret)) + goto out_err; + + req->recv = rad_acct_start_recv; + req->timeout.expire = rad_acct_start_timeout; + req->timeout.expire_tv.tv_sec = conf_timeout; + req->sent = rad_acct_start_sent; + req->log = conf_verbose ? log_ppp_info1 : NULL; - rpd->acct_req->timeout.expire = rad_acct_timeout; - rpd->acct_req->timeout.period = conf_timeout * 1000; + if (rad_req_send(req)) + goto out_err; + + rpd->acct_req = req; - rpd->acct_interim_timer.expire = rad_acct_interim_update; - rpd->acct_interim_timer.period = rpd->acct_interim_interval ? rpd->acct_interim_interval * 1000 : STAT_UPDATE_INTERVAL; - if (rpd->acct_interim_interval) - triton_timer_add(rpd->ses->ctrl->ctx, &rpd->acct_interim_timer, 0); return 0; out_err: - rad_req_free(rpd->acct_req); - rpd->acct_req = NULL; + rad_req_free(req); return -1; } -void rad_acct_stop(struct radius_pd_t *rpd) +static void rad_acct_stop_sent(struct rad_req_t *req, int res) { - int i; - time_t ts; - unsigned int dt; - - if (!rpd->acct_req || !rpd->acct_req->serv) + if (res) { + if (req->rpd) + rad_acct_stop_defer(req->rpd); + else { + if (ap_shutdown) + rad_req_free(req); + else + req->try = 0; + } + return; + } + + __sync_add_and_fetch(&req->serv->stat_acct_sent, 1); + + if (!req->hnd.tpd) { + triton_md_register_handler(req->rpd ? req->rpd->ses->ctrl->ctx : NULL, &req->hnd); + triton_md_enable_handler(&req->hnd, MD_MODE_READ); + } - if (rpd->acct_interim_timer.tpd) - triton_timer_del(&rpd->acct_interim_timer); + if (req->timeout.tpd) + triton_timer_mod(&req->timeout, 0); + else + triton_timer_add(req->rpd ? req->rpd->ses->ctrl->ctx : NULL, &req->timeout, 0); +} - if (rpd->acct_req->timeout.tpd) - rad_server_req_exit(rpd->acct_req); +static void rad_acct_stop_recv(struct rad_req_t *req) +{ + struct radius_pd_t *rpd = req->rpd; + int dt = (req->reply->tv.tv_sec - req->pack->tv.tv_sec) * 1000 + + (req->reply->tv.tv_nsec - req->pack->tv.tv_nsec) / 1000000; + + stat_accm_add(req->serv->stat_acct_query_1m, dt); + stat_accm_add(req->serv->stat_acct_query_5m, dt); + + rad_req_free(req); - if (rpd->acct_req->hnd.tpd) - triton_md_unregister_handler(&rpd->acct_req->hnd, 0); + if (rpd) + rpd->acct_req = NULL; +} + +static void rad_acct_stop_timeout(struct triton_timer_t *t) +{ + struct rad_req_t *req = container_of(t, typeof(*req), timeout); + + if (req->active) { + rad_server_req_exit(req); + rad_server_timeout(req->serv); + + __sync_add_and_fetch(&req->serv->stat_acct_lost, 1); + stat_accm_add(req->serv->stat_acct_lost_1m, 1); + stat_accm_add(req->serv->stat_acct_lost_5m, 1); - if (rpd->acct_req->timeout.tpd) - triton_timer_del(&rpd->acct_req->timeout); - - switch (rpd->ses->terminate_cause) { - case TERM_USER_REQUEST: - rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "User-Request"); - break; - case TERM_SESSION_TIMEOUT: - rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Session-Timeout"); - break; - case TERM_ADMIN_RESET: - rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Admin-Reset"); - break; - case TERM_USER_ERROR: - case TERM_AUTH_ERROR: - rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "User-Error"); - break; - case TERM_NAS_ERROR: - rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "NAS-Error"); - break; - case TERM_NAS_REQUEST: - rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "NAS-Request"); - break; - case TERM_NAS_REBOOT: - rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "NAS-Reboot"); - break; - case TERM_LOST_CARRIER: - rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Lost-Carrier"); - break; - case TERM_IDLE_TIMEOUT: - rad_packet_add_val(rpd->acct_req->pack, NULL, "Acct-Terminate-Cause", "Idle-Timeout"); - break; + if (req->before_send) + req->pack->id++; + } + + if (req->try == conf_max_try) { + rad_req_free(req); + return; + } + + if (rad_req_send(req)) { + if (ap_shutdown) { + rad_req_free(req); + return; } - rad_packet_change_val(rpd->acct_req->pack, NULL, "Acct-Status-Type", "Stop"); - req_set_stat(rpd->acct_req, rpd->ses); - req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret); - /// !!! rad_req_add_val(rpd->acct_req, "Acct-Terminate-Cause", ""); + req->try = 0; + } +} + +static void start_deferred(struct rad_req_t *req) +{ + if (req->hnd.fd != -1) { + triton_md_register_handler(NULL, &req->hnd); + triton_md_enable_handler(&req->hnd, MD_MODE_READ); + if (rad_req_read(&req->hnd)) + return; + } + + triton_timer_add(NULL, &req->timeout, 0); +} + +void rad_acct_stop_defer(struct radius_pd_t *rpd) +{ + struct rad_req_t *req = rpd->acct_req; + rad_server_req_cancel(req); + if (req->hnd.tpd) + triton_md_unregister_handler(&req->hnd, 0); + rpd->acct_req = NULL; + + req->rpd = NULL; + req->log = conf_verbose ? log_info1 : NULL; + req->timeout.expire = rad_acct_stop_timeout; + + triton_context_call(NULL, (triton_event_func)start_deferred, req); +} + +int rad_acct_stop(struct radius_pd_t *rpd) +{ + struct rad_req_t *req = rpd->acct_req; + struct timespec ts; + + if (req) { + triton_timer_del(&rpd->acct_interim_timer); - if (rpd->acct_req->reply) { - rad_packet_free(rpd->acct_req->reply); - rpd->acct_req->reply = NULL; - } + rad_server_req_cancel(req); + + clock_gettime(CLOCK_MONOTONIC, &ts); + req->ts = ts.tv_sec; + req->try = 0; + } else { + req = rad_req_alloc(rpd, CODE_ACCOUNTING_REQUEST, rpd->ses->username); + if (!req) + return -1; - time(&rpd->acct_timestamp); - - while (1) { - - if (rad_server_req_enter(rpd->acct_req)) { - if (rad_server_realloc(rpd->acct_req)) { - log_ppp_warn("radius:acct_stop: no servers available\n"); - break; - } - req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret); - continue; - } - - 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->secret)) - break; - } - if (rad_req_send(rpd->acct_req, conf_verbose ? log_ppp_info1 : NULL)) - goto out; - __sync_add_and_fetch(&rpd->acct_req->serv->stat_acct_sent, 1); - rad_req_wait(rpd->acct_req, conf_timeout); - if (!rpd->acct_req->reply) { - __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); - continue; - } - - dt = (rpd->acct_req->reply->tv.tv_sec - rpd->acct_req->pack->tv.tv_sec) * 1000 + - (rpd->acct_req->reply->tv.tv_nsec - rpd->acct_req->pack->tv.tv_nsec) / 1000000; - stat_accm_add(rpd->acct_req->serv->stat_acct_query_1m, dt); - stat_accm_add(rpd->acct_req->serv->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(&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); - } else - break; - } - -out: - rad_server_req_exit(rpd->acct_req); - - if (rpd->acct_req->reply) - break; - - rad_server_fail(rpd->acct_req->serv); - if (rad_server_realloc(rpd->acct_req)) { - log_ppp_warn("radius:acct_stop: no servers available\n"); - break; - } - req_set_RA(rpd->acct_req, rpd->acct_req->serv->secret); + if (rad_req_acct_fill(req)) { + log_ppp_error("radius:acct: failed to fill accounting attributes\n"); + rad_req_free(req); + return -1; } - rad_req_free(rpd->acct_req); - rpd->acct_req = NULL; + rpd->acct_req = req; + } + + switch (rpd->ses->terminate_cause) { + case TERM_USER_REQUEST: + rad_packet_add_val(req->pack, NULL, "Acct-Terminate-Cause", "User-Request"); + break; + case TERM_SESSION_TIMEOUT: + rad_packet_add_val(req->pack, NULL, "Acct-Terminate-Cause", "Session-Timeout"); + break; + case TERM_ADMIN_RESET: + rad_packet_add_val(req->pack, NULL, "Acct-Terminate-Cause", "Admin-Reset"); + break; + case TERM_USER_ERROR: + case TERM_AUTH_ERROR: + rad_packet_add_val(req->pack, NULL, "Acct-Terminate-Cause", "User-Error"); + break; + case TERM_NAS_ERROR: + rad_packet_add_val(req->pack, NULL, "Acct-Terminate-Cause", "NAS-Error"); + break; + case TERM_NAS_REQUEST: + rad_packet_add_val(req->pack, NULL, "Acct-Terminate-Cause", "NAS-Request"); + break; + case TERM_NAS_REBOOT: + rad_packet_add_val(req->pack, NULL, "Acct-Terminate-Cause", "NAS-Reboot"); + break; + case TERM_LOST_CARRIER: + rad_packet_add_val(req->pack, NULL, "Acct-Terminate-Cause", "Lost-Carrier"); + break; + case TERM_IDLE_TIMEOUT: + rad_packet_add_val(req->pack, NULL, "Acct-Terminate-Cause", "Idle-Timeout"); + break; + } + + rad_packet_change_val(req->pack, NULL, "Acct-Status-Type", "Stop"); + req_set_stat(req, rpd->ses); + req_set_RA(req, req->serv->secret); + + req->recv = rad_acct_stop_recv; + req->timeout.expire = rad_acct_start_timeout; + req->timeout.expire_tv.tv_sec = conf_timeout; + req->sent = rad_acct_stop_sent; + req->log = conf_verbose ? log_ppp_info1 : NULL; + + if (rad_req_send(req)) { + rad_acct_stop_defer(rpd); + return -1; + } + + return 0; } -- cgit v1.2.3