summaryrefslogtreecommitdiff
path: root/accel-pppd/radius
diff options
context:
space:
mode:
Diffstat (limited to 'accel-pppd/radius')
-rw-r--r--accel-pppd/radius/acct.c19
-rw-r--r--accel-pppd/radius/radius.c28
-rw-r--r--accel-pppd/radius/radius_p.h4
-rw-r--r--accel-pppd/radius/req.c2
-rw-r--r--accel-pppd/radius/serv.c322
5 files changed, 343 insertions, 32 deletions
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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sched.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#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);