From dc7cbe120d8794d8520e6d9d35c121474453d807 Mon Sep 17 00:00:00 2001 From: Kozlov Dmitry Date: Tue, 16 Aug 2011 17:46:25 +0400 Subject: multi-radius support --- accel-pppd/radius/acct.c | 19 +-- accel-pppd/radius/radius.c | 28 +--- accel-pppd/radius/radius_p.h | 4 +- accel-pppd/radius/req.c | 2 +- accel-pppd/radius/serv.c | 322 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 343 insertions(+), 32 deletions(-) create mode 100644 accel-pppd/radius/serv.c (limited to 'accel-pppd/radius') diff --git a/accel-pppd/radius/acct.c b/accel-pppd/radius/acct.c index 2657fb3..66dcbb4 100644 --- a/accel-pppd/radius/acct.c +++ b/accel-pppd/radius/acct.c @@ -221,7 +221,7 @@ int rad_acct_start(struct radius_pd_t *rpd) time_t ts; unsigned int dt; - if (!rpd->acct_req->serv) + if (!conf_accounting) return 0; rpd->acct_req = rad_req_alloc(rpd, CODE_ACCOUNTING_REQUEST, rpd->ppp->username); @@ -346,7 +346,7 @@ void rad_acct_stop(struct radius_pd_t *rpd) time_t ts; unsigned int dt; - if (!rpd->acct_req->serv) + if (!rpd->acct_req || !rpd->acct_req->serv) return; if (rpd->acct_interim_timer.tpd) @@ -443,14 +443,15 @@ void rad_acct_stop(struct radius_pd_t *rpd) rad_server_req_exit(rpd->acct_req); - if (!rpd->acct_req->reply) { - 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; - } - req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret); + 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; } + req_set_RA(rpd->acct_req, rpd->acct_req->serv->acct_secret); } rad_req_free(rpd->acct_req); diff --git a/accel-pppd/radius/radius.c b/accel-pppd/radius/radius.c index a6c5b93..0fc42cd 100644 --- a/accel-pppd/radius/radius.c +++ b/accel-pppd/radius/radius.c @@ -42,6 +42,9 @@ int conf_sid_in_auth; int conf_require_nas_ident; int conf_acct_interim_interval; +int conf_accounting; +int conf_fail_time = 60; + unsigned long stat_auth_sent; unsigned long stat_auth_lost; unsigned long stat_acct_sent; @@ -486,27 +489,6 @@ static int load_config(void) else if (conf_nas_ip_address) conf_bind = conf_nas_ip_address; - opt = conf_get_opt("radius", "auth-server"); - if (!opt) - opt = conf_get_opt("radius", "auth_server"); - if (!opt) { - log_emerg("radius: auth-server not specified\n"); - return -1; - } else if (parse_server(opt, &conf_auth_server, &conf_auth_server_port, &conf_auth_secret)) { - log_emerg("radius: failed to parse auth_server\n"); - return -1; - } - - opt = conf_get_opt("radius", "acct-server"); - if (!opt) - opt = conf_get_opt("radius", "acct_server"); - if (!opt) - log_emerg("radius: acct-server not specified\n"); - if (opt && parse_server(opt, &conf_acct_server, &conf_acct_server_port, &conf_acct_secret)) { - log_emerg("radius: failed to parse acct_server\n"); - return -1; - } - opt = conf_get_opt("radius", "dae-server"); if (opt && parse_server(opt, &conf_dm_coa_server, &conf_dm_coa_port, &conf_dm_coa_secret)) { log_emerg("radius: failed to parse dae-server\n"); @@ -529,6 +511,10 @@ static int load_config(void) if (opt) conf_acct_delay_time = atoi(opt); + opt = conf_get_opt("radius", "fail-time"); + if (opt) + conf_fail_time = atoi(opt); + return 0; } diff --git a/accel-pppd/radius/radius_p.h b/accel-pppd/radius/radius_p.h index 6ee130f..8c3a10d 100644 --- a/accel-pppd/radius/radius_p.h +++ b/accel-pppd/radius/radius_p.h @@ -99,6 +99,8 @@ extern int conf_require_nas_ident; extern in_addr_t conf_dm_coa_server; extern int conf_dm_coa_port; extern int conf_acct_interim_interval; +extern int conf_accounting; +extern int conf_fail_time; extern unsigned long stat_auth_sent; extern unsigned long stat_auth_lost; @@ -140,7 +142,7 @@ int rad_packet_send(struct rad_packet_t *pck, int fd, struct sockaddr_in *addr); void dm_coa_cancel(struct radius_pd_t *pd); -struct rad_server_t *rad_server_get(); +struct rad_server_t *rad_server_get(int); void rad_server_put(struct rad_server_t *); int rad_server_req_enter(struct rad_req_t *); void rad_server_req_exit(struct rad_req_t *); diff --git a/accel-pppd/radius/req.c b/accel-pppd/radius/req.c index a586111..f3d72f8 100644 --- a/accel-pppd/radius/req.c +++ b/accel-pppd/radius/req.c @@ -32,7 +32,7 @@ struct rad_req_t *rad_req_alloc(struct radius_pd_t *rpd, int code, const char *u req->hnd.fd = -1; req->ctx.before_switch = log_switch; - req->serv = rad_server_get(); + req->serv = rad_server_get(code == CODE_ACCOUNTING_REQUEST); if (!req->serv) goto out_err; diff --git a/accel-pppd/radius/serv.c b/accel-pppd/radius/serv.c new file mode 100644 index 0000000..0d9eb26 --- /dev/null +++ b/accel-pppd/radius/serv.c @@ -0,0 +1,322 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "triton.h" +#include "radius_p.h" + +#include "memdebug.h" + +static LIST_HEAD(serv_list); + +struct rad_server_t *rad_server_get(int type) +{ + struct rad_server_t *s, *s0 = NULL; + + list_for_each_entry(s, &serv_list, entry) { + if (s->fail_time && time(NULL) < s->fail_time) + continue; + + if (type == 0 && !s->auth_addr) + continue; + else if (type == 1 && !s->acct_addr) + continue; + + if (!s0) { + s0 = s; + continue; + } + + if (s->client_cnt < s0->client_cnt) + s0 = s; + } + + if (!s0) + return NULL; + + __sync_add_and_fetch(&s0->client_cnt, 1); + + return s0; +} + +void rad_server_put(struct rad_server_t *s) +{ + __sync_sub_and_fetch(&s->client_cnt, 1); +} + +int rad_server_req_enter(struct rad_req_t *req) +{ + if (!req->serv->max_req_cnt) + return 0; + + pthread_mutex_lock(&req->serv->lock); + + if (time(NULL) < req->serv->fail_time) { + pthread_mutex_unlock(&req->serv->lock); + return -1; + } + + if (req->serv->req_cnt >= req->serv->max_req_cnt) { + list_add_tail(&req->entry, &req->serv->req_queue); + pthread_mutex_unlock(&req->serv->lock); + triton_context_schedule(); + pthread_mutex_lock(&req->serv->lock); + + if (time(NULL) < req->serv->fail_time) { + pthread_mutex_unlock(&req->serv->lock); + return -1; + } + } + + req->serv->req_cnt++; + pthread_mutex_unlock(&req->serv->lock); + + return 0; +} + +void rad_server_req_exit(struct rad_req_t *req) +{ + struct rad_req_t *r = NULL; + + if (!req->serv->max_req_cnt) + return; + + pthread_mutex_lock(&req->serv->lock); + req->serv->req_cnt--; + if (req->serv->req_cnt < req->serv->max_req_cnt && !list_empty(&req->serv->req_queue)) { + r = list_entry(req->serv->req_queue.next, typeof(*r), entry); + list_del(&r->entry); + } + pthread_mutex_unlock(&req->serv->lock); + + if (r) + triton_context_wakeup(r->rpd->ppp->ctrl->ctx); +} + +int rad_server_realloc(struct rad_req_t *req, int type) +{ + if (req->hnd.fd != -1) { + close(req->hnd.fd); + req->hnd.fd = -1; + } + + if (req->serv) + rad_server_put(req->serv); + + req->serv = rad_server_get(type); + + if (!req->serv) + return -1; + + if (type) { + req->server_addr = req->serv->acct_addr; + req->server_port = req->serv->acct_port; + } else { + req->server_addr = req->serv->auth_addr; + req->server_port = req->serv->auth_port; + } + + return 0; +} + +void rad_server_fail(struct rad_server_t *s) +{ + struct rad_req_t *r; + time_t t; + + pthread_mutex_lock(&s->lock); + t = time(NULL); + + if (t > s->fail_time) { + s->fail_time = t + s->conf_fail_time; + log_ppp_warn("radius: server not responding\n"); + log_warn("radius: server noy responding\n"); + } + + while (!list_empty(&s->req_queue)) { + r = list_entry(s->req_queue.next, typeof(*r), entry); + list_del(&r->entry); + triton_context_wakeup(r->rpd->ppp->ctrl->ctx); + } + pthread_mutex_unlock(&s->lock); +} + +static void __add_server(struct rad_server_t *s) +{ + INIT_LIST_HEAD(&s->req_queue); + pthread_mutex_init(&s->lock, NULL); + s->conf_fail_time = conf_fail_time; + list_add_tail(&s->entry, &serv_list); +} + +static int parse_server_old(const char *opt, in_addr_t *addr, int *port, char **secret) +{ + char *str = _strdup(opt); + char *p1, *p2; + + p1 = strstr(str, ":"); + p2 = strstr(str, ","); + + if (p1) + *p1 = 0; + if (p2) + *p2 = 0; + else + return -1; + + *addr = inet_addr(str); + + if (p1) { + *port = atoi(p1 + 1); + if (*port <=0 ) + return -1; + } + + p1 = _strdup(p2 + 1); + p2 = *secret; + *secret = p1; + if (p2) + _free(p2); + + _free(str); + + return 0; +} + +static void add_server_old(void) +{ + const char *opt; + struct rad_server_t *s = _malloc(sizeof(*s)); + + memset(s, 0, sizeof(*s)); + + opt = conf_get_opt("radius", "auth-server"); + if (opt) { + if (parse_server_old(opt, &s->auth_addr, &s->auth_port, &s->auth_secret)) { + log_emerg("radius: failed to parse 'auth-server'\n"); + goto out; + } + } + + opt = conf_get_opt("radius", "acct-server"); + if (opt) { + if (parse_server_old(opt, &s->acct_addr, &s->acct_port, &s->acct_secret)) { + log_emerg("radius: failed to parse 'acct-server'\n"); + goto out; + } + conf_accounting = 1; + } + + if (s->auth_addr || s->acct_addr) { + __add_server(s); + return; + } + +out: + _free(s); +} + +static int parse_server(const char *_opt, struct rad_server_t *s) +{ + char *opt = _strdup(_opt); + char *ptr1, *ptr2, *ptr3, *endptr; + + ptr1 = strchr(opt, ','); + if (!ptr1) + goto out; + + ptr2 = strchr(ptr1 + 1, ','); + + if (ptr2) + ptr3 = strchr(ptr2 + 1, ','); + else + ptr3 = NULL; + + *ptr1 = 0; + if (ptr2) + *ptr2 = 0; + if (ptr3) + *ptr3 = 0; + + s->auth_addr = s->acct_addr = inet_addr(opt); + + if (ptr2) { + if (ptr2[1]) { + s->auth_port = strtol(ptr2 + 1, &endptr, 10); + if (*endptr) + goto out; + } + if (!s->auth_port) + s->auth_addr = 0; + } else + s->auth_port = 1812; + + if (ptr3) { + if (ptr3[1]) { + s->acct_port = strtol(ptr3 + 1, &endptr, 10); + if (*endptr) + goto out; + } + if (!s->acct_port) + s->acct_addr = 0; + } else + s->acct_port = 1813; + + if (!s->auth_addr && !s->acct_addr) + goto out; + + if (s->auth_addr) + s->auth_secret = _strdup(ptr1 + 1); + + if (s->acct_addr) { + s->acct_secret = _strdup(ptr1 + 1); + conf_accounting = 1; + } + + return 0; + +out: + _free(opt); + + return -1; +} + +static void add_server(const char *opt) +{ + struct rad_server_t *s = _malloc(sizeof(*s)); + + memset(s, 0, sizeof(*s)); + + if (parse_server(opt, s)) { + log_emerg("radius: failed to parse '%s'\n", opt); + _free(s); + return; + } + + __add_server(s); +} + +static void init(void) +{ + struct conf_sect_t *s = conf_get_section("radius"); + struct conf_option_t *opt; + + add_server_old(); + + + list_for_each_entry(opt, &s->items, entry) { + if (strcmp(opt->name, "server")) + continue; + add_server(opt->val); + } +} + +DEFINE_INIT(21, init); -- cgit v1.2.3