summaryrefslogtreecommitdiff
path: root/accel-pptpd/radius
diff options
context:
space:
mode:
authorKozlov Dmitry <dima@server>2010-09-09 16:10:40 +0400
committerKozlov Dmitry <dima@server>2010-09-09 16:13:25 +0400
commiteac0adf4b2b038690c761a126cb3e55a888060df (patch)
treea286e779ad7f878bc7b8eaedb99f5a64f29b1632 /accel-pptpd/radius
parent29b03dcfbd3b4783b0192e5f8c9bb6281acf44d5 (diff)
downloadaccel-ppp-eac0adf4b2b038690c761a126cb3e55a888060df.tar.gz
accel-ppp-eac0adf4b2b038690c761a126cb3e55a888060df.zip
radius: implemented DM/CoA extensions
Diffstat (limited to 'accel-pptpd/radius')
-rw-r--r--accel-pptpd/radius/CMakeLists.txt1
-rw-r--r--accel-pptpd/radius/acct.c22
-rw-r--r--accel-pptpd/radius/auth.c14
-rw-r--r--accel-pptpd/radius/dict.c8
-rw-r--r--accel-pptpd/radius/dictionary7
-rw-r--r--accel-pptpd/radius/packet.c293
-rw-r--r--accel-pptpd/radius/pd_coa.c199
-rw-r--r--accel-pptpd/radius/radius.c100
-rw-r--r--accel-pptpd/radius/radius.h48
-rw-r--r--accel-pptpd/radius/req.c213
10 files changed, 640 insertions, 265 deletions
diff --git a/accel-pptpd/radius/CMakeLists.txt b/accel-pptpd/radius/CMakeLists.txt
index 9655d91..ac8a3a9 100644
--- a/accel-pptpd/radius/CMakeLists.txt
+++ b/accel-pptpd/radius/CMakeLists.txt
@@ -6,6 +6,7 @@ SET(sources
packet.c
auth.c
acct.c
+ pd_coa.c
)
ADD_LIBRARY(radius SHARED ${sources})
diff --git a/accel-pptpd/radius/acct.c b/accel-pptpd/radius/acct.c
index b342b33..53e5e38 100644
--- a/accel-pptpd/radius/acct.c
+++ b/accel-pptpd/radius/acct.c
@@ -37,18 +37,18 @@ static void req_set_stat(struct rad_req_t *req, struct ppp_t *ppp)
return;
}
- rad_req_change_int(req, "Acct-Input-Octets", ifreq.stats.p.ppp_ibytes);
- rad_req_change_int(req, "Acct-Output-Octets", ifreq.stats.p.ppp_obytes);
- rad_req_change_int(req, "Acct-Input-Packets", ifreq.stats.p.ppp_ipackets);
- rad_req_change_int(req, "Acct-Output-Packets", ifreq.stats.p.ppp_opackets);
- rad_req_change_int(req, "Acct-Session-Time", time(NULL) - ppp->start_time);
+ rad_packet_change_int(req->pack, "Acct-Input-Octets", ifreq.stats.p.ppp_ibytes);
+ rad_packet_change_int(req->pack, "Acct-Output-Octets", ifreq.stats.p.ppp_obytes);
+ rad_packet_change_int(req->pack, "Acct-Input-Packets", ifreq.stats.p.ppp_ipackets);
+ rad_packet_change_int(req->pack, "Acct-Output-Packets", ifreq.stats.p.ppp_opackets);
+ rad_packet_change_int(req->pack, "Acct-Session-Time", time(NULL) - ppp->start_time);
}
static int rad_acct_read(struct triton_md_handler_t *h)
{
struct rad_req_t *req = container_of(h, typeof(*req), hnd);
- req->reply = rad_packet_recv(h->fd);
+ req->reply = rad_packet_recv(h->fd, NULL);
if (!req->reply)
return 0;
@@ -83,9 +83,9 @@ static void rad_acct_interim_update(struct triton_timer_t *t)
if (rpd->acct_req->timeout.period)
return;
- rad_req_change_val(rpd->acct_req, "Acct-Status-Type", "Interim-Update", 4);
+ rad_packet_change_val(rpd->acct_req->pack, "Acct-Status-Type", "Interim-Update");
req_set_stat(rpd->acct_req, rpd->ppp);
- req_set_RA(rpd->acct_req, conf_acct_server_secret);
+ req_set_RA(rpd->acct_req, conf_acct_secret);
rad_req_send(rpd->acct_req);
rpd->acct_req->timeout.period = conf_timeout * 1000;
triton_timer_add(rpd->ppp->ctrl->ctx, &rpd->acct_req->timeout, 0);
@@ -109,7 +109,7 @@ int rad_acct_start(struct radius_pd_t *rpd)
//if (rad_req_add_str(rpd->acct_req, "Acct-Session-Id", rpd->ppp->sessionid, PPP_SESSIONID_LEN, 1))
// goto out_err;
- if (req_set_RA(rpd->acct_req, conf_acct_server_secret))
+ if (req_set_RA(rpd->acct_req, conf_acct_secret))
goto out_err;
if (rad_req_send(rpd->acct_req))
@@ -155,9 +155,9 @@ void rad_acct_stop(struct radius_pd_t *rpd)
if (rpd->acct_req->timeout.period)
triton_timer_del(&rpd->acct_req->timeout);
- rad_req_change_val(rpd->acct_req, "Acct-Status-Type", "Stop", 4);
+ rad_packet_change_val(rpd->acct_req->pack, "Acct-Status-Type", "Stop");
req_set_stat(rpd->acct_req, rpd->ppp);
- req_set_RA(rpd->acct_req, conf_acct_server_secret);
+ req_set_RA(rpd->acct_req, conf_acct_secret);
/// !!! rad_req_add_val(rpd->acct_req, "Acct-Terminate-Cause", "");
for(i = 0; i < conf_max_try; i++) {
if (rad_req_send(rpd->acct_req))
diff --git a/accel-pptpd/radius/auth.c b/accel-pptpd/radius/auth.c
index 98c7aa8..2415a05 100644
--- a/accel-pptpd/radius/auth.c
+++ b/accel-pptpd/radius/auth.c
@@ -56,11 +56,11 @@ int rad_auth_pap(struct radius_pd_t *rpd, const char *username, va_list args)
if (!req)
return PWDB_DENIED;
- epasswd = encrypt_password(passwd, conf_auth_server_secret, req->RA, &epasswd_len);
+ epasswd = encrypt_password(passwd, conf_auth_secret, req->RA, &epasswd_len);
if (!epasswd)
goto out;
- if (rad_req_add_str(req, "Password", (char*)epasswd, epasswd_len, 0)) {
+ if (rad_packet_add_octets(req->pack, "Password", epasswd, epasswd_len)) {
free(epasswd);
goto out;
}
@@ -97,12 +97,12 @@ int rad_auth_chap_md5(struct radius_pd_t *rpd, const char *username, va_list arg
{
struct rad_req_t *req;
int i, r = PWDB_DENIED;
- char chap_password[17];
+ uint8_t chap_password[17];
int id = va_arg(args, int);
- const uint8_t *challenge = va_arg(args, const uint8_t *);
+ uint8_t *challenge = va_arg(args, uint8_t *);
int challenge_len = va_arg(args, int);
- const uint8_t *response = va_arg(args, const uint8_t *);
+ uint8_t *response = va_arg(args, uint8_t *);
chap_password[0] = id;
memcpy(chap_password + 1, response, 16);
@@ -114,11 +114,11 @@ int rad_auth_chap_md5(struct radius_pd_t *rpd, const char *username, va_list arg
if (challenge_len == 16)
memcpy(req->RA, challenge, 16);
else {
- if (rad_req_add_str(req, "CHAP-Challenge", (char*)challenge, challenge_len, 0))
+ if (rad_packet_add_octets(req->pack, "CHAP-Challenge", challenge, challenge_len))
goto out;
}
- if (rad_req_add_str(req, "CHAP-Password", chap_password, 17, 0))
+ if (rad_packet_add_octets(req->pack, "CHAP-Password", chap_password, 17))
goto out;
for(i = 0; i < conf_max_try; i++) {
diff --git a/accel-pptpd/radius/dict.c b/accel-pptpd/radius/dict.c
index d76c2c3..4a0435a 100644
--- a/accel-pptpd/radius/dict.c
+++ b/accel-pptpd/radius/dict.c
@@ -42,10 +42,10 @@ static int split(char *buf, char **ptr)
}
buf = skip_word(buf);
- if (*buf == '\n')
+ //if (*buf == '\n')
*buf = 0;
- else if (*buf)
- return -1;
+ //else if (*buf)
+ // return -1;
return 0;
}
@@ -124,6 +124,8 @@ int rad_dict_load(const char *fname)
attr->type = ATTR_TYPE_DATE;
else if (!strcmp(ptr[2], "ipaddr"))
attr->type = ATTR_TYPE_IPADDR;
+ else if (!strcmp(ptr[2], "octets"))
+ attr->type = ATTR_TYPE_OCTETS;
else {
log_error("radius:%s:%i: unknown attribute type\n", fname, n);
goto out_err;
diff --git a/accel-pptpd/radius/dictionary b/accel-pptpd/radius/dictionary
index 25896eb..223cc26 100644
--- a/accel-pptpd/radius/dictionary
+++ b/accel-pptpd/radius/dictionary
@@ -27,8 +27,8 @@
# Following are the proper new names. Use these.
#
ATTRIBUTE User-Name 1 string
-ATTRIBUTE Password 2 string
-ATTRIBUTE CHAP-Password 3 string
+ATTRIBUTE Password 2 octets
+ATTRIBUTE CHAP-Password 3 octets
ATTRIBUTE NAS-IP-Address 4 ipaddr
ATTRIBUTE NAS-Port-Id 5 integer
ATTRIBUTE Service-Type 6 integer
@@ -83,6 +83,7 @@ ATTRIBUTE Login-LAT-Port 63 integer
ATTRIBUTE Connect-Info 77 string
ATTRIBUTE Acct-Interim-Interval 85 integer
+ATTRIBUTE Error-Cause 101 integer
#
# RFC3162 IPv6 attributes
@@ -186,6 +187,7 @@ VALUE NAS-Port-Type Sync 1
VALUE NAS-Port-Type ISDN 2
VALUE NAS-Port-Type ISDN-V120 3
VALUE NAS-Port-Type ISDN-V110 4
+VALUE NAS-Port-Type Virtual 5
# Acct Terminate Causes, available in 3.3.2 and later
@@ -239,4 +241,3 @@ VALUE Add-Port-To-IP-Address Yes 1
#VALUE Server-Config Password-Expiration 30
#VALUE Server-Config Password-Warning 5
-
diff --git a/accel-pptpd/radius/packet.c b/accel-pptpd/radius/packet.c
index e88e6e8..dcc1053 100644
--- a/accel-pptpd/radius/packet.c
+++ b/accel-pptpd/radius/packet.c
@@ -39,7 +39,7 @@ void print_buf(uint8_t *buf,int size)
int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA)
{
- struct rad_req_attr_t *attr;
+ struct rad_attr_t *attr;
uint8_t *ptr;
if (pack->buf)
@@ -65,6 +65,7 @@ int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA)
case ATTR_TYPE_INTEGER:
*(uint32_t*)ptr = htonl(attr->val.integer);
break;
+ case ATTR_TYPE_OCTETS:
case ATTR_TYPE_STRING:
memcpy(ptr, attr->val.string, attr->len);
break;
@@ -85,13 +86,14 @@ int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA)
return 0;
}
-struct rad_packet_t *rad_packet_recv(int fd)
+struct rad_packet_t *rad_packet_recv(int fd, struct sockaddr_in *addr)
{
struct rad_packet_t *pack;
- struct rad_req_attr_t *attr;
+ struct rad_attr_t *attr;
struct rad_dict_attr_t *da;
uint8_t *ptr;
int n, id, len;
+ socklen_t addr_len = sizeof(*addr);
pack = rad_packet_alloc(0);
if (!pack)
@@ -104,7 +106,10 @@ struct rad_packet_t *rad_packet_recv(int fd)
}
while (1) {
- n = read(fd, pack->buf, REQ_LENGTH_MAX);
+ if (addr)
+ n = recvfrom(fd, pack->buf, REQ_LENGTH_MAX, 0, addr, &addr_len);
+ else
+ n = read(fd, pack->buf, REQ_LENGTH_MAX);
if (n < 0) {
if (errno == EINTR)
continue;
@@ -164,6 +169,15 @@ struct rad_packet_t *rad_packet_recv(int fd)
memcpy(attr->val.string, ptr, len);
attr->val.string[len] = 0;
break;
+ case ATTR_TYPE_OCTETS:
+ attr->val.octets = malloc(len);
+ if (!attr->val.octets) {
+ log_error("radius:packet: out of memory\n");
+ free(attr);
+ goto out_err;
+ }
+ memcpy(attr->val.octets, ptr, len);
+ break;
case ATTR_TYPE_DATE:
case ATTR_TYPE_INTEGER:
attr->val.integer = ntohl(*(uint32_t*)ptr);
@@ -188,15 +202,15 @@ out_err:
void rad_packet_free(struct rad_packet_t *pack)
{
- struct rad_req_attr_t *attr;
+ struct rad_attr_t *attr;
if (pack->buf)
free(pack->buf);
while(!list_empty(&pack->attrs)) {
attr = list_entry(pack->attrs.next, typeof(*attr), entry);
- if (attr->attr->type == ATTR_TYPE_STRING)
- free((char*)attr->val.string);
+ if (attr->attr->type == ATTR_TYPE_STRING || attr->attr->type == ATTR_TYPE_OCTETS)
+ free(attr->val.string);
list_del(&attr->entry);
free(attr);
}
@@ -206,7 +220,7 @@ void rad_packet_free(struct rad_packet_t *pack)
void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt, ...))
{
- struct rad_req_attr_t *attr;
+ struct rad_attr_t *attr;
struct rad_dict_value_t *val;
print("[RADIUS ");
@@ -229,6 +243,24 @@ void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt,
case CODE_ACCOUNTING_RESPONSE:
printf("Accounting-Response");
break;
+ case CODE_DISCONNECT_REQUEST:
+ printf("Disconnect-Request");
+ break;
+ case CODE_DISCONNECT_ACK:
+ printf("Disconnect-ACK");
+ break;
+ case CODE_DISCONNECT_NAK:
+ printf("Disconnect-NAK");
+ break;
+ case CODE_COA_REQUEST:
+ printf("CoA-Request");
+ break;
+ case CODE_COA_ACK:
+ printf("CoA-ACK");
+ break;
+ case CODE_COA_NAK:
+ printf("CoA-NAK");
+ break;
default:
print("Unknown (%i)", pack->code);
}
@@ -236,25 +268,238 @@ void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt,
list_for_each_entry(attr, &pack->attrs, entry) {
print(" <%s ", attr->attr->name);
- if (attr->printable) {
- switch (attr->attr->type) {
- case ATTR_TYPE_INTEGER:
- val = rad_dict_find_val(attr->attr, attr->val);
- if (val)
- print("%s", val->name);
- else
- print("%i", attr->val.integer);
- break;
- case ATTR_TYPE_STRING:
- print("\"%s\"", attr->val.string);
- break;
- case ATTR_TYPE_IPADDR:
- print("%i.%i.%i.%i", attr->val.ipaddr & 0xff, (attr->val.ipaddr >> 8) & 0xff, (attr->val.ipaddr >> 16) & 0xff, (attr->val.ipaddr >> 24) & 0xff);
- break;
- }
+ switch (attr->attr->type) {
+ case ATTR_TYPE_INTEGER:
+ val = rad_dict_find_val(attr->attr, attr->val);
+ if (val)
+ print("%s", val->name);
+ else
+ print("%i", attr->val.integer);
+ break;
+ case ATTR_TYPE_STRING:
+ print("\"%s\"", attr->val.string);
+ break;
+ case ATTR_TYPE_IPADDR:
+ print("%i.%i.%i.%i", attr->val.ipaddr & 0xff, (attr->val.ipaddr >> 8) & 0xff, (attr->val.ipaddr >> 16) & 0xff, (attr->val.ipaddr >> 24) & 0xff);
+ break;
}
print(">");
}
print("]\n");
}
+int rad_packet_add_int(struct rad_packet_t *pack, const char *name, int val)
+{
+ struct rad_attr_t *ra;
+ struct rad_dict_attr_t *attr;
+
+ if (pack->len + 2 + 4 >= REQ_LENGTH_MAX)
+ return -1;
+
+ attr = rad_dict_find_attr(name);
+ if (!attr)
+ return -1;
+
+ ra = malloc(sizeof(*ra));
+ if (!ra)
+ return -1;
+
+ ra->attr = attr;
+ ra->len = 4;
+ ra->val.integer = val;
+ list_add_tail(&ra->entry, &pack->attrs);
+ pack->len += 2 + 4;
+
+ return 0;
+}
+
+int rad_packet_change_int(struct rad_packet_t *pack, const char *name, int val)
+{
+ struct rad_attr_t *ra;
+
+ ra = rad_packet_find_attr(pack, name);
+ if (!ra)
+ return -1;
+
+ ra->val.integer = val;
+
+ return 0;
+}
+
+int rad_packet_add_octets(struct rad_packet_t *pack, const char *name, uint8_t *val, int len)
+{
+ struct rad_attr_t *ra;
+ struct rad_dict_attr_t *attr;
+
+ if (pack->len + 2 + len >= REQ_LENGTH_MAX)
+ return -1;
+
+ attr = rad_dict_find_attr(name);
+ if (!attr)
+ return -1;
+
+ ra = malloc(sizeof(*ra));
+ if (!ra) {
+ log_error("radius: out of memory\n");
+ return -1;
+ }
+
+ ra->attr = attr;
+ ra->len = len;
+ ra->val.octets = malloc(len);
+ if (!ra->val.octets) {
+ log_error("radius: out of memory\n");
+ free(ra);
+ return -1;
+ }
+ memcpy(ra->val.octets, val, len);
+ list_add_tail(&ra->entry, &pack->attrs);
+ pack->len += 2 + len;
+
+ return 0;
+}
+int rad_packet_add_str(struct rad_packet_t *pack, const char *name, const char *val, int len)
+{
+ struct rad_attr_t *ra;
+ struct rad_dict_attr_t *attr;
+
+ if (pack->len + 2 + len >= REQ_LENGTH_MAX)
+ return -1;
+
+ attr = rad_dict_find_attr(name);
+ if (!attr)
+ return -1;
+
+ ra = malloc(sizeof(*ra));
+ if (!ra) {
+ log_error("radius: out of memory\n");
+ return -1;
+ }
+
+ ra->attr = attr;
+ ra->len = len;
+ ra->val.string = malloc(len+1);
+ if (!ra->val.string) {
+ log_error("radius: out of memory\n");
+ free(ra);
+ return -1;
+ }
+ memcpy(ra->val.string, val, len);
+ ra->val.string[len] = 0;
+ list_add_tail(&ra->entry, &pack->attrs);
+ pack->len += 2 + len;
+
+ return 0;
+}
+
+int rad_packet_change_str(struct rad_packet_t *pack, const char *name, const char *val, int len)
+{
+ struct rad_attr_t *ra;
+
+ ra = rad_packet_find_attr(pack, name);
+ if (!ra)
+ return -1;
+
+ if (ra->len != len) {
+ if (pack->len - ra->len + len >= REQ_LENGTH_MAX)
+ return -1;
+
+ ra->val.string = realloc(ra->val.string, len + 1);
+ if (!ra->val.string) {
+ log_error("radius: out of memory\n");
+ return -1;
+ }
+
+ pack->len += len - ra->len;
+ ra->len = len;
+ }
+
+ memcpy(ra->val.string, val, len);
+ ra->val.string[len] = 0;
+
+ return 0;
+}
+
+int rad_packet_add_val(struct rad_packet_t *pack, const char *name, const char *val)
+{
+ struct rad_attr_t *ra;
+ struct rad_dict_attr_t *attr;
+ struct rad_dict_value_t *v;
+
+ if (pack->len + 2 + 4 >= REQ_LENGTH_MAX)
+ return -1;
+
+ attr = rad_dict_find_attr(name);
+ if (!attr)
+ return -1;
+
+ v = rad_dict_find_val_name(attr, val);
+ if (!v)
+ return -1;
+
+ ra = malloc(sizeof(*ra));
+ if (!ra)
+ return -1;
+
+ ra->attr = attr;
+ ra->len = 4;
+ ra->val = v->val;
+ list_add_tail(&ra->entry, &pack->attrs);
+ pack->len += 2 + 4;
+
+ return 0;
+}
+
+int rad_packet_change_val(struct rad_packet_t *pack, const char *name, const char *val)
+{
+ struct rad_attr_t *ra;
+ struct rad_dict_value_t *v;
+
+ ra = rad_packet_find_attr(pack, name);
+ if (!ra)
+ return -1;
+
+ v = rad_dict_find_val_name(ra->attr, val);
+ if (!v)
+ return -1;
+
+ ra->val = v->val;
+
+ return 0;
+}
+
+struct rad_attr_t *rad_packet_find_attr(struct rad_packet_t *pack, const char *name)
+{
+ struct rad_attr_t *ra;
+
+ list_for_each_entry(ra, &pack->attrs, entry)
+ if (!strcmp(ra->attr->name, name))
+ return ra;
+
+ return NULL;
+}
+
+int rad_packet_send(struct rad_packet_t *pack, int fd, struct sockaddr_in *addr)
+{
+ int n;
+
+ while (1) {
+ if (addr)
+ n = sendto(fd, pack->buf, pack->len, 0, addr, sizeof(*addr));
+ else
+ n = write(fd, pack->buf, pack->len);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+ log_error("radius:write: %s\n", strerror(errno));
+ return -1;
+ } else if (n != pack->len) {
+ log_error("radius:write: short write %i, excpected %i\n", n, pack->len);
+ return -1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
diff --git a/accel-pptpd/radius/pd_coa.c b/accel-pptpd/radius/pd_coa.c
new file mode 100644
index 0000000..b19575c
--- /dev/null
+++ b/accel-pptpd/radius/pd_coa.c
@@ -0,0 +1,199 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <openssl/md5.h>
+
+#include "triton.h"
+#include "log.h"
+
+#include "radius.h"
+
+#define PD_COA_PORT 3799
+
+struct pd_coa_serv_t
+{
+ struct triton_context_t ctx;
+ struct triton_md_handler_t hnd;
+};
+
+static int pd_coa_check_RA(struct rad_packet_t *pack, const char *secret)
+{
+ uint8_t RA[16];
+ MD5_CTX ctx;
+
+ memset(RA, 0, 16);
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, pack->buf, 4);
+ MD5_Update(&ctx, RA, 16);
+ MD5_Update(&ctx, pack->buf + 20, pack->len - 20);
+ MD5_Update(&ctx, secret, strlen(secret));
+ MD5_Final(RA, &ctx);
+
+ return memcmp(RA, pack->buf + 4, 16);
+}
+
+static void pd_coa_set_RA(struct rad_packet_t *pack, const char *secret)
+{
+ MD5_CTX ctx;
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, pack->buf, pack->len);
+ MD5_Update(&ctx, secret, strlen(secret));
+ MD5_Final(pack->buf + 4, &ctx);
+}
+
+static void disconnect_request(struct radius_pd_t *rpd)
+{
+ rad_packet_free(rpd->pd_coa_req);
+ rpd->pd_coa_req = NULL;
+
+ ppp_terminate(rpd->ppp, 0);
+}
+
+static void coa_request(struct radius_pd_t *rpd)
+{
+ rad_packet_free(rpd->pd_coa_req);
+ rpd->pd_coa_req = NULL;
+
+/// TODO: CoA handling
+}
+
+static int pd_coa_read(struct triton_md_handler_t *h)
+{
+ struct rad_packet_t *pack;
+ struct rad_packet_t *reply = NULL;
+ struct radius_pd_t *rpd;
+ int err_code;
+ uint8_t RA[16];
+ struct sockaddr_in addr;
+
+
+ pack = rad_packet_recv(h->fd, &addr);
+ if (!pack)
+ return 0;
+
+ if (pack->code != CODE_DISCONNECT_REQUEST && pack->code != CODE_COA_REQUEST) {
+ log_warn("radius:pd_coa: unexpected code (%i) received\n", pack->code);
+ goto out_err_no_reply;
+ }
+
+ if (pd_coa_check_RA(pack, conf_pd_coa_secret)) {
+ log_warn("radius:pd_coa: RA validation failed\n");
+ goto out_err_no_reply;
+ }
+
+ memcpy(RA, pack->buf + 4, sizeof(RA));
+
+ if (conf_verbose) {
+ log_debug("recv ");
+ rad_packet_print(pack, log_debug);
+ }
+
+ if (rad_check_nas_pack(pack)) {
+ log_warn("radius:pd_coa: NAS identification failed\n");
+ err_code = 403;
+ goto out_err;
+ }
+
+ rpd = rad_find_session_pack(pack);
+ if (!rpd) {
+ log_warn("radius:pd_coa: session not found\n");
+ err_code = 503;
+ goto out_err;
+ }
+
+ rpd->pd_coa_req = pack;
+
+ if (pack->code == CODE_DISCONNECT_REQUEST)
+ triton_context_call(rpd->ppp->ctrl->ctx, (void (*)(void *))disconnect_request, rpd);
+ else
+ triton_context_call(rpd->ppp->ctrl->ctx, (void (*)(void *))coa_request, rpd);
+
+ pthread_mutex_unlock(&rpd->lock);
+
+ reply = rad_packet_alloc(pack->code == CODE_COA_REQUEST ? CODE_COA_ACK : CODE_DISCONNECT_ACK);
+ reply->id = pack->id;
+ if (rad_packet_build(reply, RA))
+ goto out_err_no_reply;
+ pd_coa_set_RA(reply, conf_pd_coa_secret);
+ if (conf_verbose) {
+ log_debug("send ");
+ rad_packet_print(reply, log_debug);
+ }
+ rad_packet_send(reply, h->fd, &addr);
+ rad_packet_free(reply);
+
+ return 0;
+
+out_err:
+ reply = rad_packet_alloc(pack->code == CODE_COA_REQUEST ? CODE_COA_NAK : CODE_DISCONNECT_NAK);
+ rad_packet_add_int(reply, "Error-Cause", err_code);
+ reply->id = pack->id;
+ if (rad_packet_build(reply, RA))
+ goto out_err_no_reply;
+ pd_coa_set_RA(reply, conf_pd_coa_secret);
+ if (conf_verbose) {
+ log_debug("send ");
+ rad_packet_print(reply, log_debug);
+ }
+ rad_packet_send(reply, h->fd, &addr);
+
+out_err_no_reply:
+ rad_packet_free(pack);
+ if (reply)
+ rad_packet_free(reply);
+ return 0;
+}
+
+static void pd_coa_close(struct triton_context_t *ctx)
+{
+ struct pd_coa_serv_t *serv = container_of(ctx, typeof(*serv), ctx);
+ triton_md_unregister_handler(&serv->hnd);
+ close(serv->hnd.fd);
+}
+
+static struct pd_coa_serv_t serv = {
+ .ctx.close = pd_coa_close,
+ .hnd.read = pd_coa_read,
+};
+
+static void __init init(void)
+{
+ struct sockaddr_in addr;
+
+ serv.hnd.fd = socket (PF_INET, SOCK_DGRAM, 0);
+ if (serv.hnd.fd < 0) {
+ log_error("radius:pd_coa: socket: %s\n", strerror(errno));
+ return;
+ }
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons (PD_COA_PORT);
+ if (conf_nas_ip_address)
+ addr.sin_addr.s_addr = inet_addr(conf_nas_ip_address);
+ else
+ addr.sin_addr.s_addr = htonl (INADDR_ANY);
+ if (bind (serv.hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
+ log_error("radius:pd_coa: bind: %s\n", strerror(errno));
+ close(serv.hnd.fd);
+ return;
+ }
+
+ if (fcntl(serv.hnd.fd, F_SETFL, O_NONBLOCK)) {
+ log_error("radius:pd_coa: failed to set nonblocking mode: %s\n", strerror(errno));
+ close(serv.hnd.fd);
+ return;
+ }
+
+ triton_context_register(&serv.ctx);
+ triton_md_register_handler(&serv.ctx, &serv.hnd);
+ triton_md_enable_handler(&serv.hnd, MD_MODE_READ);
+}
diff --git a/accel-pptpd/radius/radius.c b/accel-pptpd/radius/radius.c
index 89491b3..de64f6b 100644
--- a/accel-pptpd/radius/radius.c
+++ b/accel-pptpd/radius/radius.c
@@ -25,17 +25,21 @@ int conf_verbose = 0;
char *conf_auth_server;
int conf_auth_server_port = 1812;
-char *conf_auth_server_secret;
+char *conf_auth_secret;
char *conf_acct_server;
int conf_acct_server_port = 1813;
-char *conf_acct_server_secret;
+char *conf_acct_secret;
+char *conf_pd_coa_secret;
+
+static LIST_HEAD(sessions);
+static pthread_rwlock_t sessions_lock = PTHREAD_RWLOCK_INITIALIZER;
static struct ppp_notified_t notified;
void rad_proc_attrs(struct rad_req_t *req)
{
- struct rad_req_attr_t *attr;
+ struct rad_attr_t *attr;
list_for_each_entry(attr, &req->reply->attrs, entry) {
if (!strcmp(attr->attr->name, "Framed-IP-Address"))
@@ -102,7 +106,12 @@ static void ppp_starting(struct ppp_notified_t *n, struct ppp_t *ppp)
memset(pd, 0, sizeof(*pd));
pd->pd.key = n;
pd->ppp = ppp;
+ pthread_mutex_init(&pd->lock, NULL);
list_add_tail(&pd->pd.entry, &ppp->pd_list);
+
+ pthread_rwlock_wrlock(&sessions_lock);
+ list_add_tail(&pd->entry, &sessions);
+ pthread_rwlock_unlock(&sessions_lock);
}
static void ppp_started(struct ppp_notified_t *n, struct ppp_t *ppp)
@@ -122,6 +131,15 @@ static void ppp_finished(struct ppp_notified_t *n, struct ppp_t *ppp)
{
struct radius_pd_t *rpd = find_pd(ppp);
+ pthread_rwlock_wrlock(&sessions_lock);
+ pthread_mutex_lock(&rpd->lock);
+ list_del(&rpd->entry);
+ pthread_mutex_unlock(&rpd->lock);
+ pthread_rwlock_unlock(&sessions_lock);
+
+ if (rpd->pd_coa_req)
+ rad_packet_free(rpd->pd_coa_req);
+
list_del(&rpd->pd.entry);
free(rpd);
}
@@ -142,6 +160,74 @@ struct radius_pd_t *find_pd(struct ppp_t *ppp)
}
+struct radius_pd_t *rad_find_session(const char *sessionid, const char *username, int port_id, in_addr_t ipaddr)
+{
+ struct radius_pd_t *rpd;
+
+ pthread_rwlock_rdlock(&sessions_lock);
+ list_for_each_entry(rpd, &sessions, entry) {
+ if (sessionid && strcmp(sessionid, rpd->ppp->sessionid))
+ continue;
+ if (username && strcmp(username, rpd->ppp->username))
+ continue;
+ if (port_id >= 0 && port_id != rpd->ppp->unit_idx)
+ continue;
+ if (ipaddr && ipaddr != rpd->ipaddr)
+ continue;
+ pthread_mutex_lock(&rpd->lock);
+ pthread_rwlock_unlock(&sessions_lock);
+ return rpd;
+ }
+ pthread_rwlock_unlock(&sessions_lock);
+ return NULL;
+}
+
+struct radius_pd_t *rad_find_session_pack(struct rad_packet_t *pack)
+{
+ struct rad_attr_t *attr;
+ const char *sessionid = NULL;
+ const char *username = NULL;
+ int port_id = -1;
+ in_addr_t ipaddr = 0;
+
+ list_for_each_entry(attr, &pack->attrs, entry) {
+ if (!strcmp(attr->attr->name, "Acct-Session-Id"))
+ sessionid = attr->val.string;
+ else if (!strcmp(attr->attr->name, "User-Name"))
+ username = attr->val.string;
+ else if (!strcmp(attr->attr->name, "NAS-Port-Id"))
+ port_id = attr->val.integer;
+ else if (!strcmp(attr->attr->name, "Framed-IP-Address"))
+ ipaddr = attr->val.ipaddr;
+ }
+
+ if (username && !sessionid)
+ return NULL;
+
+ return rad_find_session(sessionid, username, port_id, ipaddr);
+}
+
+int rad_check_nas_pack(struct rad_packet_t *pack)
+{
+ struct rad_attr_t *attr;
+ const char *ident = NULL;
+ in_addr_t ipaddr = 0;
+
+ list_for_each_entry(attr, &pack->attrs, entry) {
+ if (!strcmp(attr->attr->name, "NAS-Identifier"))
+ ident = attr->val.string;
+ else if (!strcmp(attr->attr->name, "NAS-IP-Address"))
+ ipaddr = attr->val.ipaddr;
+ }
+
+ if (conf_nas_identifier && (!ident || strcmp(conf_nas_identifier, ident)))
+ return -1;
+ if (conf_nas_ip_address && inet_addr(conf_nas_ip_address) != ipaddr)
+ return -1;
+
+ return 0;
+}
+
static struct ipdb_t ipdb = {
.get = get_ip,
};
@@ -211,17 +297,21 @@ static void __init radius_init(void)
if (!opt) {
log_error("radius: auth_server not specified\n");
_exit(EXIT_FAILURE);
- } else if (parse_server(opt, &conf_auth_server, &conf_auth_server_port, &conf_auth_server_secret)) {
+ } else if (parse_server(opt, &conf_auth_server, &conf_auth_server_port, &conf_auth_secret)) {
log_error("radius: failed to parse auth_server\n");
_exit(EXIT_FAILURE);
}
opt = conf_get_opt("radius", "acct_server");
- if (opt && parse_server(opt, &conf_acct_server, &conf_acct_server_port, &conf_acct_server_secret)) {
+ if (opt && parse_server(opt, &conf_acct_server, &conf_acct_server_port, &conf_acct_secret)) {
log_error("radius: failed to parse acct_server\n");
_exit(EXIT_FAILURE);
}
+ opt = conf_get_opt("radius", "pd_coa_secret");
+ if (opt)
+ conf_pd_coa_secret = opt;
+
opt = conf_get_opt("radius", "dictionary");
if (!opt) {
fprintf(stderr, "radius: dictionary not specified\n");
diff --git a/accel-pptpd/radius/radius.h b/accel-pptpd/radius/radius.h
index c167151..9728a4a 100644
--- a/accel-pptpd/radius/radius.h
+++ b/accel-pptpd/radius/radius.h
@@ -3,6 +3,8 @@
#include <stdint.h>
#include <netinet/in.h>
+#include <pthread.h>
+
#include "triton.h"
#include "ppp.h"
@@ -10,25 +12,35 @@
#define ATTR_TYPE_INTEGER 0
#define ATTR_TYPE_STRING 1
-#define ATTR_TYPE_DATE 2
-#define ATTR_TYPE_IPADDR 3
+#define ATTR_TYPE_OCTETS 2
+#define ATTR_TYPE_DATE 3
+#define ATTR_TYPE_IPADDR 4
#define CODE_ACCESS_REQUEST 1
#define CODE_ACCESS_ACCEPT 2
#define CODE_ACCESS_REJECT 3
#define CODE_ACCESS_CHALLENGE 11
-#define CODE_ACCOUNTING_REQUEST 4
+#define CODE_ACCOUNTING_REQUEST 4
#define CODE_ACCOUNTING_RESPONSE 5
+#define CODE_DISCONNECT_REQUEST 40
+#define CODE_DISCONNECT_ACK 41
+#define CODE_DISCONNECT_NAK 42
+#define CODE_COA_REQUEST 43
+#define CODE_COA_ACK 44
+#define CODE_COA_NAK 45
struct radius_pd_t
{
+ struct list_head entry;
struct ppp_pd_t pd;
struct ppp_t *ppp;
+ pthread_mutex_t lock;
struct rad_req_t *acct_req;
struct triton_timer_t acct_interim_timer;
+ struct rad_packet_t *pd_coa_req;
in_addr_t ipaddr;
int acct_interim_interval;
@@ -38,6 +50,7 @@ typedef union
{
int integer;
char *string;
+ uint8_t *octets;
time_t date;
in_addr_t ipaddr;
} rad_value_t;
@@ -63,14 +76,13 @@ struct rad_dict_attr_t
struct list_head values;
};
-struct rad_req_attr_t
+struct rad_attr_t
{
struct list_head entry;
struct rad_dict_attr_t *attr;
//struct rad_dict_value_t *val;
rad_value_t val;
int len;
- int printable:1;
};
struct rad_packet_t
@@ -103,11 +115,16 @@ extern char *conf_nas_identifier;
extern char *conf_nas_ip_address;
extern char *conf_gw_ip_address;
extern char *conf_auth_server;
-extern char *conf_auth_server_secret;
+extern char *conf_auth_secret;
extern int conf_auth_server_port;
extern char *conf_acct_server;
-extern char *conf_acct_server_secret;
+extern char *conf_acct_secret;
extern int conf_acct_server_port;
+extern char *conf_pd_coa_secret;
+
+int rad_check_nas_pack(struct rad_packet_t *pack);
+struct radius_pd_t *rad_find_session(const char *sessionid, const char *username, int port_id, in_addr_t ipaddr);
+struct radius_pd_t *rad_find_session_pack(struct rad_packet_t *pack);
int rad_dict_load(const char *fname);
void rad_dict_free(struct rad_dict_t *dict);
@@ -121,18 +138,21 @@ int rad_req_acct_fill(struct rad_req_t *);
void rad_req_free(struct rad_req_t *);
int rad_req_send(struct rad_req_t *);
int rad_req_wait(struct rad_req_t *, int);
-struct rad_req_attr_t *rad_req_find_attr(struct rad_req_t *req, const char *name);
-int rad_req_add_int(struct rad_req_t *req, const char *name, int val);
-int rad_req_add_val(struct rad_req_t *req, const char *name, const char *val, int len);
-int rad_req_add_str(struct rad_req_t *req, const char *name, const char *val, int len, int printable);
-int rad_req_change_int(struct rad_req_t *req, const char *name, int val);
-int rad_req_change_val(struct rad_req_t *req, const char *name, const char *val, int len);
+
+struct rad_attr_t *rad_packet_find_attr(struct rad_packet_t *pack, const char *name);
+int rad_packet_add_int(struct rad_packet_t *pack, const char *name, int val);
+int rad_packet_add_val(struct rad_packet_t *pack, const char *name, const char *val);
+int rad_packet_add_str(struct rad_packet_t *pack, const char *name, const char *val, int len);
+int rad_packet_add_octets(struct rad_packet_t *pack, const char *name, uint8_t *val, int len);
+int rad_packet_change_int(struct rad_packet_t *pack, const char *name, int val);
+int rad_packet_change_val(struct rad_packet_t *pack, const char *name, const char *val);
struct rad_packet_t *rad_packet_alloc(int code);
int rad_packet_build(struct rad_packet_t *pack, uint8_t *RA);
-struct rad_packet_t *rad_packet_recv(int fd);
+struct rad_packet_t *rad_packet_recv(int fd, struct sockaddr_in *addr);
void rad_packet_free(struct rad_packet_t *);
void rad_packet_print(struct rad_packet_t *pack, void (*print)(const char *fmt, ...));
+int rad_packet_send(struct rad_packet_t *pck, int fd, struct sockaddr_in *addr);
struct radius_pd_t *find_pd(struct ppp_t *ppp);
void rad_proc_attrs(struct rad_req_t *req);
diff --git a/accel-pptpd/radius/req.c b/accel-pptpd/radius/req.c
index 3d71bf8..40397a8 100644
--- a/accel-pptpd/radius/req.c
+++ b/accel-pptpd/radius/req.c
@@ -43,18 +43,18 @@ struct rad_req_t *rad_req_alloc(struct radius_pd_t *rpd, int code, const char *u
if (!req->pack)
goto out_err;
- if (rad_req_add_str(req, "User-Name", username, strlen(username), 1))
+ if (rad_packet_add_str(req->pack, "User-Name", username, strlen(username)))
goto out_err;
if (conf_nas_identifier)
- if (rad_req_add_str(req, "NAS-Identifier", conf_nas_identifier, strlen(conf_nas_identifier), 1))
+ if (rad_packet_add_str(req->pack, "NAS-Identifier", conf_nas_identifier, strlen(conf_nas_identifier)))
goto out_err;
- if (rad_req_add_int(req, "NAS-Port-Id", rpd->ppp->unit_idx))
+ if (rad_packet_add_int(req->pack, "NAS-Port-Id", rpd->ppp->unit_idx))
goto out_err;
- if (rad_req_add_val(req, "NAS-Port-Type", "Sync", 4))
+ if (rad_packet_add_val(req->pack, "NAS-Port-Type", "Virtual"))
goto out_err;
- if (rad_req_add_val(req, "Service-Type", "Framed-User", 4))
+ if (rad_packet_add_val(req->pack, "Service-Type", "Framed-User"))
goto out_err;
- if (rad_req_add_val(req, "Framed-Protocol", "PPP", 4))
+ if (rad_packet_add_val(req->pack, "Framed-Protocol", "PPP"))
goto out_err;
return req;
@@ -71,19 +71,19 @@ int rad_req_acct_fill(struct rad_req_t *req)
memset(req->RA, 0, sizeof(req->RA));
- if (rad_req_add_val(req, "Acct-Status-Type", "Start", 4))
+ if (rad_packet_add_val(req->pack, "Acct-Status-Type", "Start"))
return -1;
- if (rad_req_add_str(req, "Acct-Session-Id", req->rpd->ppp->sessionid, PPP_SESSIONID_LEN, 1))
+ if (rad_packet_add_str(req->pack, "Acct-Session-Id", req->rpd->ppp->sessionid, PPP_SESSIONID_LEN))
return -1;
- if (rad_req_add_int(req, "Acct-Session-Time", 0))
+ if (rad_packet_add_int(req->pack, "Acct-Session-Time", 0))
return -1;
- if (rad_req_add_int(req, "Acct-Input-Octets", 0))
+ if (rad_packet_add_int(req->pack, "Acct-Input-Octets", 0))
return -1;
- if (rad_req_add_int(req, "Acct-Output-Octets", 0))
+ if (rad_packet_add_int(req->pack, "Acct-Output-Octets", 0))
return -1;
- if (rad_req_add_int(req, "Acct-Input-Packets", 0))
+ if (rad_packet_add_int(req->pack, "Acct-Input-Packets", 0))
return -1;
- if (rad_req_add_int(req, "Acct-Output-Packets", 0))
+ if (rad_packet_add_int(req->pack, "Acct-Output-Packets", 0))
return -1;
return 0;
@@ -144,8 +144,6 @@ out_err:
int rad_req_send(struct rad_req_t *req)
{
- int n;
-
if (req->hnd.fd == -1 && make_socket(req))
return -1;
@@ -157,20 +155,7 @@ int rad_req_send(struct rad_req_t *req)
rad_packet_print(req->pack, log_debug);
}
- while (1) {
- n = write(req->hnd.fd, req->pack->buf, req->pack->len);
- //n = sendto(req->hnd.fd, req->pack->buf, req->pack->len, 0, &addr, sizeof(addr));
- if (n < 0) {
- if (errno == EINTR)
- continue;
- log_error("radius:write: %s\n", strerror(errno));
- goto out_err;
- } else if (n != req->pack->len) {
- log_error("radius:write: short write %i, excpected %i\n", n, req->pack->len);
- goto out_err;
- }
- break;
- }
+ rad_packet_send(req->pack, req->hnd.fd, NULL);
return 0;
@@ -180,174 +165,6 @@ out_err:
return -1;
}
-int rad_req_add_int(struct rad_req_t *req, const char *name, int val)
-{
- struct rad_req_attr_t *ra;
- struct rad_dict_attr_t *attr;
-
- if (req->pack->len + 2 + 4 >= REQ_LENGTH_MAX)
- return -1;
-
- attr = rad_dict_find_attr(name);
- if (!attr)
- return -1;
-
- ra = malloc(sizeof(*ra));
- if (!ra)
- return -1;
-
- ra->attr = attr;
- ra->len = 4;
- ra->val.integer = val;
- ra->printable = 1;
- list_add_tail(&ra->entry, &req->pack->attrs);
- req->pack->len += 2 + 4;
-
- return 0;
-}
-
-int rad_req_change_int(struct rad_req_t *req, const char *name, int val)
-{
- struct rad_req_attr_t *ra;
-
- ra = rad_req_find_attr(req, name);
- if (!ra)
- return -1;
-
- ra->val.integer = val;
-
- return 0;
-}
-
-int rad_req_add_str(struct rad_req_t *req, const char *name, const char *val, int len, int printable)
-{
- struct rad_req_attr_t *ra;
- struct rad_dict_attr_t *attr;
-
- if (req->pack->len + 2 + len >= REQ_LENGTH_MAX)
- return -1;
-
- attr = rad_dict_find_attr(name);
- if (!attr)
- return -1;
-
- ra = malloc(sizeof(*ra));
- if (!ra) {
- log_error("radius: aout of memory\n");
- return -1;
- }
-
- ra->attr = attr;
- ra->len = len;
- ra->val.string = malloc(len+1);
- if (!ra->val.string) {
- log_error("radius: out of memory\n");
- free(ra);
- return -1;
- }
- memcpy(ra->val.string, val, len);
- ra->val.string[len] = 0;
- ra->printable = printable;
- list_add_tail(&ra->entry, &req->pack->attrs);
- req->pack->len += 2 + len;
-
- return 0;
-}
-
-int rad_req_change_str(struct rad_req_t *req, const char *name, const char *val, int len)
-{
- struct rad_req_attr_t *ra;
-
- ra = rad_req_find_attr(req, name);
- if (!ra)
- return -1;
-
- if (ra->len != len) {
- if (req->pack->len - ra->len + len >= REQ_LENGTH_MAX)
- return -1;
-
- ra->val.string = realloc(ra->val.string, len + 1);
- if (!ra->val.string) {
- log_error("radius: out of memory\n");
- return -1;
- }
-
- req->pack->len += len - ra->len;
- ra->len = len;
- }
-
- memcpy(ra->val.string, val, len);
- ra->val.string[len] = 0;
-
- return 0;
-}
-
-int rad_req_add_val(struct rad_req_t *req, const char *name, const char *val, int len)
-{
- struct rad_req_attr_t *ra;
- struct rad_dict_attr_t *attr;
- struct rad_dict_value_t *v;
-
- if (req->pack->len + 2 + len >= REQ_LENGTH_MAX)
- return -1;
-
- attr = rad_dict_find_attr(name);
- if (!attr)
- return -1;
-
- v = rad_dict_find_val_name(attr, val);
- if (!v)
- return -1;
-
- ra = malloc(sizeof(*ra));
- if (!ra)
- return -1;
-
- ra->attr = attr;
- ra->len = len;
- ra->val = v->val;
- ra->printable = 1;
- list_add_tail(&ra->entry, &req->pack->attrs);
- req->pack->len += 2 + len;
-
- return 0;
-}
-
-int rad_req_change_val(struct rad_req_t *req, const char *name, const char *val, int len)
-{
- struct rad_req_attr_t *ra;
- struct rad_dict_value_t *v;
-
- ra = rad_req_find_attr(req, name);
- if (!ra)
- return -1;
-
- v = rad_dict_find_val_name(ra->attr, val);
- if (!v)
- return -1;
-
- if (req->pack->len - ra->len + len >= REQ_LENGTH_MAX)
- return -1;
-
- req->pack->len += len - ra->len;
-
- ra->len = len;
- ra->val = v->val;
-
- return 0;
-}
-
-struct rad_req_attr_t *rad_req_find_attr(struct rad_req_t *req, const char *name)
-{
- struct rad_req_attr_t *ra;
-
- list_for_each_entry(ra, &req->pack->attrs, entry)
- if (!strcmp(ra->attr->name, name))
- return ra;
-
- return NULL;
-}
-
static void req_wakeup(struct rad_req_t *req)
{
triton_context_wakeup(req->rpd->ppp->ctrl->ctx);
@@ -359,7 +176,7 @@ static int rad_req_read(struct triton_md_handler_t *h)
{
struct rad_req_t *req = container_of(h, typeof(*req), hnd);
- req->reply = rad_packet_recv(h->fd);
+ req->reply = rad_packet_recv(h->fd, NULL);
req_wakeup(req);
return 0;